mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 17:34:43 +00:00
Merge pull request #1218 from rbehrends/parsefloat-fix
Fixed parsing of float literals.
This commit is contained in:
@@ -231,33 +231,43 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||
else:
|
||||
number = int(res)
|
||||
|
||||
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.
|
||||
## 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
|
||||
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
|
||||
sign = 1.0
|
||||
t = ""
|
||||
hasdigits = false
|
||||
|
||||
# Sign?
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
if 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':
|
||||
@@ -265,6 +275,8 @@ proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
|
||||
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':
|
||||
@@ -272,46 +284,40 @@ proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
|
||||
number = Inf*sign
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
# Integer part?
|
||||
while s[i] in {'0'..'9'}:
|
||||
# Read integer part
|
||||
flags = flags or 1
|
||||
number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
|
||||
hasdigits = true
|
||||
add(t, s[i])
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
# Decimal?
|
||||
|
||||
# Fractional part?
|
||||
if s[i] == '.':
|
||||
var hd = 1.0
|
||||
add(t, localeconv().decimal_point)
|
||||
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
|
||||
hasdigits = true
|
||||
add(t, s[i])
|
||||
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
|
||||
if not hasdigits:
|
||||
return 0
|
||||
|
||||
# Exponent?
|
||||
if s[i] in {'e', 'E'}:
|
||||
add(t, s[i])
|
||||
inc(i)
|
||||
if s[i] == '+':
|
||||
inc(i)
|
||||
elif s[i] == '-':
|
||||
esign = -1.0
|
||||
if s[i] in {'+', '-'}:
|
||||
add(t, s[i])
|
||||
inc(i)
|
||||
if s[i] notin {'0'..'9'}:
|
||||
return 0
|
||||
while s[i] in {'0'..'9'}:
|
||||
exponent = exponent * 10 + ord(s[i]) - ord('0')
|
||||
add(t, s[i])
|
||||
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
|
||||
number = strtod(t, nil)
|
||||
result = i - start
|
||||
|
||||
proc parseFloat*(s: string, number: var float, start = 0): int {.
|
||||
|
||||
42
tests/float/tfloat4.nim
Normal file
42
tests/float/tfloat4.nim
Normal file
@@ -0,0 +1,42 @@
|
||||
import math, strutils
|
||||
|
||||
proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", varargs.}
|
||||
|
||||
proc floatToStr(f: float64): string =
|
||||
var buffer: array[128, char]
|
||||
c_sprintf(buffer, "%.16e", f)
|
||||
result = ""
|
||||
for ch in buffer:
|
||||
if ch == '\0':
|
||||
return
|
||||
add(result, ch)
|
||||
|
||||
let testFloats = [
|
||||
"0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99",
|
||||
"1.1e10", "-2e100", "1.234e-10", "1.234e+10",
|
||||
"-inf", "inf", "+inf",
|
||||
"3.14159265358979323846264338327950288",
|
||||
"1.57079632679489661923132169163975144",
|
||||
"0.785398163397448309615660845819875721",
|
||||
"1.41421356237309504880168872420969808",
|
||||
"0.707106781186547524400844362104849039",
|
||||
"2.71828182845904523536028747135266250",
|
||||
"0.00097656250000000021684043449710088680149056017398834228515625"
|
||||
]
|
||||
|
||||
for num in testFloats:
|
||||
assert num.parseFloat.floatToStr.parseFloat == num.parseFloat
|
||||
|
||||
assert "0".parseFloat == 0.0
|
||||
assert "-.1".parseFloat == -0.1
|
||||
assert "2.5e1".parseFloat == 25.0
|
||||
assert "1e10".parseFloat == 10_000_000_000.0
|
||||
assert "0.000_005".parseFloat == 5.000_000e-6
|
||||
assert "1.234_567e+2".parseFloat == 123.4567
|
||||
assert "1e1_00".parseFloat == "1e100".parseFloat
|
||||
assert "3.1415926535897932384626433".parseFloat ==
|
||||
3.1415926535897932384626433
|
||||
assert "2.71828182845904523536028747".parseFloat ==
|
||||
2.71828182845904523536028747
|
||||
assert 0.00097656250000000021684043449710088680149056017398834228515625 ==
|
||||
"0.00097656250000000021684043449710088680149056017398834228515625".parseFloat
|
||||
Reference in New Issue
Block a user