consistent floating point output (#12219)
* unify float printing * makes tests green
This commit is contained in:
parent
04c803d6df
commit
7bc5bf8334
4 changed files with 81 additions and 34 deletions
59
lib/system/formatfloat.nim
Normal file
59
lib/system/formatfloat.nim
Normal file
|
|
@ -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
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
include inclrtl
|
include inclrtl
|
||||||
|
import formatfloat
|
||||||
|
|
||||||
# ----------------- IO Part ------------------------------------------------
|
# ----------------- IO Part ------------------------------------------------
|
||||||
type
|
type
|
||||||
|
|
@ -350,10 +351,16 @@ proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} =
|
||||||
proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
|
proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
|
||||||
if b: write(f, "true")
|
if b: write(f, "true")
|
||||||
else: write(f, "false")
|
else: write(f, "false")
|
||||||
|
|
||||||
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
|
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.} =
|
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.} =
|
proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} =
|
||||||
discard c_putc(cint(c), f)
|
discard c_putc(cint(c), f)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,15 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||||
result = newStringOfCap(sizeof(x)*4)
|
result = newStringOfCap(sizeof(x)*4)
|
||||||
result.addInt x
|
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) =
|
proc addFloat*(result: var string; x: float) =
|
||||||
## Converts float to its string representation and appends it to `result`.
|
## Converts float to its string representation and appends it to `result`.
|
||||||
##
|
##
|
||||||
|
|
@ -85,37 +94,9 @@ proc addFloat*(result: var string; x: float) =
|
||||||
when nimvm:
|
when nimvm:
|
||||||
result.add $x
|
result.add $x
|
||||||
else:
|
else:
|
||||||
var buf: array[0..64, char]
|
var buffer: array[65, char]
|
||||||
when defined(nimNoArrayToCstringConversion):
|
let n = writeFloatToBuffer(buffer, x)
|
||||||
var n: int = c_sprintf(addr buf, "%.16g", x)
|
result.addCstringN(cstring(buffer[0].addr), n)
|
||||||
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
|
|
||||||
|
|
||||||
proc add*(result: var string; x: float) {.deprecated:
|
proc add*(result: var string; x: float) {.deprecated:
|
||||||
"Deprecated since v0.20, use 'addFloat'".} =
|
"Deprecated since v0.20, use 'addFloat'".} =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
output: "111"
|
output: "1.01.01.0"
|
||||||
"""
|
"""
|
||||||
# Test overloading of [] with multiple indices
|
# Test overloading of [] with multiple indices
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue