summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2014-07-15 21:50:17 +0200
committerAndreas Rumpf <rumpf_a@web.de>2014-07-15 21:50:17 +0200
commit85ec4dc4cb5ce4e20902966df19f19191ceac205 (patch)
tree1b7f60ed2528fbc2d2a98667c523f23f7c08d9e6 /lib
parent021db192a03640b0f94caf6d7abd775cbbd4b0a5 (diff)
parent8b8a21cb592cdff15d8e448945b3d019eab42564 (diff)
downloadNim-85ec4dc4cb5ce4e20902966df19f19191ceac205.tar.gz
Merge pull request #1218 from rbehrends/parsefloat-fix
Fixed parsing of float literals.
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/parseutils.nim94
1 files changed, 50 insertions, 44 deletions
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 484ba5184..9b3f79a74 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -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 {.