summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-09-20 10:41:40 +0200
committerGitHub <noreply@github.com>2019-09-20 10:41:40 +0200
commit7bc5bf83345a0ebaef3dca9395f0bc7d285ba61e (patch)
tree1a83ef297e0e78b0c3a29d7d87b035e168d242b9 /lib
parent04c803d6df63bebd709a203c9055b21750c7927e (diff)
downloadNim-7bc5bf83345a0ebaef3dca9395f0bc7d285ba61e.tar.gz
consistent floating point output (#12219)
* unify float printing
* makes tests green
Diffstat (limited to 'lib')
-rw-r--r--lib/system/formatfloat.nim59
-rw-r--r--lib/system/io.nim11
-rw-r--r--lib/system/strmantle.nim43
3 files changed, 80 insertions, 33 deletions
diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim
new file mode 100644
index 000000000..dbe9a4145
--- /dev/null
+++ b/lib/system/formatfloat.nim
@@ -0,0 +1,59 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
+                                    importc: "sprintf", varargs, noSideEffect.}
+
+proc writeToBuffer(buf: var array[65, char]; value: cstring) =
+  var i = 0
+  while value[i] != '\0':
+    buf[i] = value[i]
+    inc i
+
+proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int =
+  ## This is the implementation to format floats in the Nim
+  ## programming language. The specific format for floating point
+  ## numbers is not specified in the Nim programming language and
+  ## might change slightly in the future, but at least wherever you
+  ## format a float, it should be consistent.
+  ##
+  ## returns the amount of bytes written to `buf` not counting the
+  ## terminating '\0' character.
+  ##
+  ## * `buf` - A buffer to write into. The buffer does not need to be
+  ##           initialized and it will be overridden.
+  ##
+  var n: int = c_sprintf(addr buf, "%.16g", value)
+  var hasDot = false
+  for i in 0..n-1:
+    if buf[i] == ',':
+      buf[i] = '.'
+      hasDot = true
+    elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+      hasDot = true
+  if not hasDot:
+    buf[n] = '.'
+    buf[n+1] = '0'
+    buf[n+2] = '\0'
+    result = n + 2
+  else:
+    result = n
+  # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
+  # of '-1.#IND' are produced.
+  # We want to get rid of these here:
+  if buf[n-1] in {'n', 'N', 'D', 'd'}:
+    writeToBuffer(buf, "nan")
+    result = 3
+  elif buf[n-1] == 'F':
+    if buf[0] == '-':
+      writeToBuffer(buf, "-inf")
+      result = 4
+    else:
+      writeToBuffer(buf, "inf")
+      result = 3
diff --git a/lib/system/io.nim b/lib/system/io.nim
index a6eeee7c7..a39e8cf94 100644
--- a/lib/system/io.nim
+++ b/lib/system/io.nim
@@ -8,6 +8,7 @@
 #
 
 include inclrtl
+import formatfloat
 
 # ----------------- IO Part ------------------------------------------------
 type
@@ -350,10 +351,16 @@ proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} =
 proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
   if b: write(f, "true")
   else: write(f, "false")
+
 proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
-  if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
+  var buffer: array[65, char]
+  discard writeFloatToBuffer(buffer, r)
+  if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
+
 proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} =
-  if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
+  var buffer: array[65, char]
+  discard writeFloatToBuffer(buffer, r)
+  if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
 
 proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} =
   discard c_putc(cint(c), f)
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 863eddd17..69e0d8052 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -74,6 +74,15 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
   result = newStringOfCap(sizeof(x)*4)
   result.addInt x
 
+proc addCstringN(result: var string, buf: cstring; buflen: int) =
+  # no nimvm support needed, so it doesn't need to be fast here either
+  let oldLen = result.len
+  let newLen = oldLen + buflen
+  result.setLen newLen
+  copyMem(result[oldLen].addr, buf, buflen)
+
+import formatfloat
+
 proc addFloat*(result: var string; x: float) =
   ## Converts float to its string representation and appends it to `result`.
   ##
@@ -85,37 +94,9 @@ proc addFloat*(result: var string; x: float) =
   when nimvm:
     result.add $x
   else:
-    var buf: array[0..64, char]
-    when defined(nimNoArrayToCstringConversion):
-      var n: int = c_sprintf(addr buf, "%.16g", x)
-    else:
-      var n: int = c_sprintf(buf, "%.16g", x)
-    var hasDot = false
-    for i in 0..n-1:
-      if buf[i] == ',':
-        buf[i] = '.'
-        hasDot = true
-      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
-        hasDot = true
-    if not hasDot:
-      buf[n] = '.'
-      buf[n+1] = '0'
-      buf[n+2] = '\0'
-    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
-    # of '-1.#IND' are produced.
-    # We want to get rid of these here:
-    if buf[n-1] in {'n', 'N', 'D', 'd'}:
-      result.add "nan"
-    elif buf[n-1] == 'F':
-      if buf[0] == '-':
-        result.add "-inf"
-      else:
-        result.add "inf"
-    else:
-      var i = 0
-      while buf[i] != '\0':
-        result.add buf[i]
-        inc i
+    var buffer: array[65, char]
+    let n = writeFloatToBuffer(buffer, x)
+    result.addCstringN(cstring(buffer[0].addr), n)
 
 proc add*(result: var string; x: float) {.deprecated:
   "Deprecated since v0.20, use 'addFloat'".} =