diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2019-09-20 10:41:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-20 10:41:40 +0200 |
commit | 7bc5bf83345a0ebaef3dca9395f0bc7d285ba61e (patch) | |
tree | 1a83ef297e0e78b0c3a29d7d87b035e168d242b9 /lib | |
parent | 04c803d6df63bebd709a203c9055b21750c7927e (diff) | |
download | Nim-7bc5bf83345a0ebaef3dca9395f0bc7d285ba61e.tar.gz |
consistent floating point output (#12219)
* unify float printing * makes tests green
Diffstat (limited to 'lib')
-rw-r--r-- | lib/system/formatfloat.nim | 59 | ||||
-rw-r--r-- | lib/system/io.nim | 11 | ||||
-rw-r--r-- | lib/system/strmantle.nim | 43 |
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'".} = |