mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
consistent floating point output (#12219)
* unify float printing * makes tests green
This commit is contained in:
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
|
||||
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)
|
||||
|
||||
@@ -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'".} =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
output: "111"
|
||||
output: "1.01.01.0"
|
||||
"""
|
||||
# Test overloading of [] with multiple indices
|
||||
|
||||
|
||||
Reference in New Issue
Block a user