diff options
author | Araq <rumpf_a@web.de> | 2014-07-16 00:41:03 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-07-16 00:41:03 +0200 |
commit | a7911addf71fdfbf5008afd938b49116d462d908 (patch) | |
tree | d1ce9e4306882f722da587bce7992ebe632aab24 /lib | |
parent | 396b5619fc72ccc4a2f2110fd59b21e0a8a7250e (diff) | |
download | Nim-a7911addf71fdfbf5008afd938b49116d462d908.tar.gz |
parseBiggestFloat is now builtin
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pure/parseutils.nim | 166 | ||||
-rw-r--r-- | lib/system/ansi_c.nim | 5 | ||||
-rw-r--r-- | lib/system/jssys.nim | 84 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 111 |
4 files changed, 274 insertions, 92 deletions
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 9b3f79a74..06ee07aa8 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -231,94 +231,96 @@ proc parseInt*(s: string, number: var int, start = 0): int {. else: number = int(res) -proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. - rtl, extern: "npuParseBiggestFloat", noSideEffect.} = - ## parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if a parsing error - ## occurred. - - type struct_lconv {.importc: "struct lconv",header:"<locale.h>".} = - object - # Unneeded fields have been omitted. - decimal_point: cstring - - proc localeconv(): ptr struct_lconv {.importc, header: "<locale.h>", - noSideEffect.} - - proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, - header: "<stdlib.h>", noSideEffect.} - - # This routine leverages `strtod()` for the non-trivial task of - # parsing floating point numbers correctly. Because `strtod()` is - # locale-dependent with respect to the radix character, we create - # a copy where the decimal point is replaced with the locale's - # radix character. - - var - i = start - sign = 1.0 - t = "" - hasdigits = false - - # Sign? - if s[i] == '+' or s[i] == '-': - if s[i] == '-': +when defined(nimParseBiggestFloatMagic): + proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. + magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.} + ## parses a float starting at `start` and stores the value into `number`. + ## Result is the number of processed chars or 0 if a parsing error + ## occurred. +else: + proc tenToThePowerOf(b: int): BiggestFloat = + var b = b + var a = 10.0 + result = 1.0 + while true: + if (b and 1) == 1: + result *= a + b = b shr 1 + if b == 0: break + a *= a + + proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. + rtl, extern: "npuParseBiggestFloat", noSideEffect.} = + ## parses a float starting at `start` and stores the value into `number`. + ## Result is the number of processed chars or 0 if there occured a parsing + ## error. + var + esign = 1.0 + sign = 1.0 + i = start + exponent: int + flags: int + number = 0.0 + if s[i] == '+': inc(i) + elif s[i] == '-': sign = -1.0 - add(t, s[i]) - inc(i) - - # NaN? - if s[i] == 'N' or s[i] == 'n': - if s[i+1] == 'A' or s[i+1] == 'a': - if s[i+2] == 'N' or s[i+2] == 'n': - if s[i+3] notin IdentChars: - number = NaN - return i+3 - start - return 0 - - # Inf? - if s[i] == 'I' or s[i] == 'i': - if s[i+1] == 'N' or s[i+1] == 'n': - if s[i+2] == 'F' or s[i+2] == 'f': - if s[i+3] notin IdentChars: - number = Inf*sign - return i+3 - start - return 0 - - # Integer part? - while s[i] in {'0'..'9'}: - hasdigits = true - add(t, s[i]) - inc(i) - while s[i] == '_': inc(i) - - # Fractional part? - if s[i] == '.': - add(t, localeconv().decimal_point) - inc(i) - while s[i] in {'0'..'9'}: - hasdigits = true - add(t, s[i]) inc(i) - while s[i] == '_': inc(i) - if not hasdigits: - return 0 - - # Exponent? - if s[i] in {'e', 'E'}: - add(t, s[i]) - inc(i) - if s[i] in {'+', '-'}: - add(t, s[i]) - inc(i) - if s[i] notin {'0'..'9'}: + if s[i] == 'N' or s[i] == 'n': + if s[i+1] == 'A' or s[i+1] == 'a': + if s[i+2] == 'N' or s[i+2] == 'n': + if s[i+3] notin IdentChars: + number = NaN + return i+3 - start + return 0 + if s[i] == 'I' or s[i] == 'i': + if s[i+1] == 'N' or s[i+1] == 'n': + if s[i+2] == 'F' or s[i+2] == 'f': + if s[i+3] notin IdentChars: + number = Inf*sign + return i+3 - start return 0 while s[i] in {'0'..'9'}: - add(t, s[i]) + # Read integer part + flags = flags or 1 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) inc(i) while s[i] == '_': inc(i) - number = strtod(t, nil) - result = i - start + # Decimal? + if s[i] == '.': + var hd = 1.0 + inc(i) + while s[i] in {'0'..'9'}: + # Read fractional part + flags = flags or 2 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) + hd = hd * 10.0 + inc(i) + while s[i] == '_': inc(i) + number = number / hd # this complicated way preserves precision + # Again, read integer and fractional part + if flags == 0: return 0 + # Exponent? + if s[i] in {'e', 'E'}: + inc(i) + if s[i] == '+': + inc(i) + elif s[i] == '-': + esign = -1.0 + inc(i) + if s[i] notin {'0'..'9'}: + return 0 + while s[i] in {'0'..'9'}: + exponent = exponent * 10 + ord(s[i]) - ord('0') + inc(i) + while s[i] == '_': inc(i) + # Calculate Exponent + let hd = tenToThePowerOf(exponent) + if esign > 0.0: number = number * hd + else: number = number / hd + # evaluate sign + number = number * sign + result = i - start + proc parseFloat*(s: string, number: var float, start = 0): int {. rtl, extern: "npuParseFloat", noSideEffect.} = diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index da101cc2c..511a006d3 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -106,13 +106,10 @@ proc c_fopen(filename, mode: cstring): C_TextFileStar {. importc: "fopen", header: "<stdio.h>".} proc c_fclose(f: C_TextFileStar) {.importc: "fclose", header: "<stdio.h>".} -proc c_sprintf(buf, frmt: cstring): int {.header: "<stdio.h>", +proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", importc: "sprintf", varargs, noSideEffect.} # we use it only in a way that cannot lead to security issues -proc c_localeconv():ptr cstring {.header: "<locale.h>", - importc: "localeconv", noSideEffect.} - proc c_fread(buf: pointer, size, n: int, f: C_BinaryFileStar): int {. importc: "fread", header: "<stdio.h>".} proc c_fseek(f: C_BinaryFileStar, offset: clong, whence: int): int {. diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 52f8873cf..1196a81ef 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -639,3 +639,87 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = """ {.pop.} + +proc tenToThePowerOf(b: int): BiggestFloat = + var b = b + var a = 10.0 + result = 1.0 + while true: + if (b and 1) == 1: + result *= a + b = b shr 1 + if b == 0: break + a *= a + +const + IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + +# XXX use JS's native way here +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {. + compilerProc.} = + var + esign = 1.0 + sign = 1.0 + i = start + exponent: int + flags: int + number = 0.0 + if s[i] == '+': inc(i) + elif s[i] == '-': + sign = -1.0 + inc(i) + if s[i] == 'N' or s[i] == 'n': + if s[i+1] == 'A' or s[i+1] == 'a': + if s[i+2] == 'N' or s[i+2] == 'n': + if s[i+3] notin IdentChars: + number = NaN + return i+3 - start + return 0 + if s[i] == 'I' or s[i] == 'i': + if s[i+1] == 'N' or s[i+1] == 'n': + if s[i+2] == 'F' or s[i+2] == 'f': + if s[i+3] notin IdentChars: + number = Inf*sign + return i+3 - start + return 0 + while s[i] in {'0'..'9'}: + # Read integer part + flags = flags or 1 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) + inc(i) + while s[i] == '_': inc(i) + # Decimal? + if s[i] == '.': + var hd = 1.0 + inc(i) + while s[i] in {'0'..'9'}: + # Read fractional part + flags = flags or 2 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) + hd = hd * 10.0 + inc(i) + while s[i] == '_': inc(i) + number = number / hd # this complicated way preserves precision + # Again, read integer and fractional part + if flags == 0: return 0 + # Exponent? + if s[i] in {'e', 'E'}: + inc(i) + if s[i] == '+': + inc(i) + elif s[i] == '-': + esign = -1.0 + inc(i) + if s[i] notin {'0'..'9'}: + return 0 + while s[i] in {'0'..'9'}: + exponent = exponent * 10 + ord(s[i]) - ord('0') + inc(i) + while s[i] == '_': inc(i) + # Calculate Exponent + let hd = tenToThePowerOf(exponent) + if esign > 0.0: number = number * hd + else: number = number / hd + # evaluate sign + number = number * sign + result = i - start diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 183ea0c8d..b3dc9c14e 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -252,15 +252,114 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = proc nimFloatToStr(f: float): string {.compilerproc.} = var buf: array [0..64, char] - var n:int = c_sprintf(buf, "%.16g", f) + var n: int = c_sprintf(buf, "%.16g", f) + var hasDot = false for i in 0..n-1: - if buf[i] notin {'0'..'9','-'}: - return $buf - buf[n] = c_localeconv()[0] - buf[n+1] = '0' - buf[n+2] = '\0' + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'e', 'E', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' result = $buf +proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, + header: "<stdlib.h>", noSideEffect.} + +var decimalPoint: char + +proc getDecimalPoint(): char = + result = decimalPoint + if result == '\0': + if strtod("0,5", nil) == 0.5: result = ',' + else: result = '.' + # yes this is threadsafe in practice, spare me: + decimalPoint = result + +const + IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, + start = 0): int {.compilerProc.} = + # This routine leverages `strtod()` for the non-trivial task of + # parsing floating point numbers correctly. Because `strtod()` is + # locale-dependent with respect to the radix character, we create + # a copy where the decimal point is replaced with the locale's + # radix character. + var + i = start + sign = 1.0 + t: array[128, char] + ti = 0 + hasdigits = false + + template addToBuf(c) = + if ti < t.high: + t[ti] = c; inc(ti) + + # Sign? + if s[i] == '+' or s[i] == '-': + if s[i] == '-': + sign = -1.0 + t[ti] = s[i] + inc(i); inc(ti) + + # NaN? + if s[i] == 'N' or s[i] == 'n': + if s[i+1] == 'A' or s[i+1] == 'a': + if s[i+2] == 'N' or s[i+2] == 'n': + if s[i+3] notin IdentChars: + number = NaN + return i+3 - start + return 0 + + # Inf? + if s[i] == 'I' or s[i] == 'i': + if s[i+1] == 'N' or s[i+1] == 'n': + if s[i+2] == 'F' or s[i+2] == 'f': + if s[i+3] notin IdentChars: + number = Inf*sign + return i+3 - start + return 0 + + # Integer part? + while s[i] in {'0'..'9'}: + hasdigits = true + addToBuf(s[i]) + inc(i); + while s[i] == '_': inc(i) + + # Fractional part? + if s[i] == '.': + addToBuf(getDecimalPoint()) + inc(i) + while s[i] in {'0'..'9'}: + hasdigits = true + addToBuf(s[i]) + inc(i) + while s[i] == '_': inc(i) + if not hasdigits: + return 0 + + # Exponent? + if s[i] in {'e', 'E'}: + addToBuf(s[i]) + inc(i) + if s[i] in {'+', '-'}: + addToBuf(s[i]) + inc(i) + if s[i] notin {'0'..'9'}: + return 0 + while s[i] in {'0'..'9'}: + addToBuf(s[i]) + inc(i) + while s[i] == '_': inc(i) + number = strtod(t, nil) + result = i - start + proc nimInt64ToStr(x: int64): string {.compilerRtl.} = result = newString(sizeof(x)*4) var i = 0 |