parseBiggestFloat is now builtin
This commit is contained in:
parent
396b5619fc
commit
a7911addf7
10 changed files with 309 additions and 95 deletions
|
|
@ -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 {.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue