use dragonbox algorithm; alternative to #18008 (#18139)

* use dragonbox algorithm; alternative to #18008
* removed unsafe code
This commit is contained in:
Andreas Rumpf 2021-06-01 22:29:53 +02:00 • committed by GitHub
commit 63db2b19bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1408 additions and 50 deletions

View file

@ -76,6 +76,10 @@
- `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that - `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that
`%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json.
- `system.addFloat` now uses the "Dragonbox" algorithm, which ensures correct roundtrips of floating point
numbers, that the minimum length representation of a floating point number is used and correct rounding.
Use `-d:nimLegacyAddFloat` for a transition period.
- `strformat` is now part of `include std/prelude`. - `strformat` is now part of `include std/prelude`.
- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. - Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`.

View file

@ -134,3 +134,5 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasUnifiedTuple")
defineSymbol("nimHasIterable") defineSymbol("nimHasIterable")
defineSymbol("nimHasTypeofVoid") defineSymbol("nimHasTypeofVoid")
defineSymbol("nimHasDragonBox")

1347
lib/system/dragonbox.nim Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,53 +7,58 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", when not defined(nimLegacyAddFloat) and not defined(nimscript) and
importc: "sprintf", varargs, noSideEffect.} not defined(js) and defined(nimHasDragonBox):
import dragonbox
proc writeToBuffer(buf: var array[65, char]; value: cstring) = proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int =
var i = 0 ## This is the implementation to format floats.
while value[i] != '\0': ##
buf[i] = value[i] ## returns the amount of bytes written to `buf` not counting the
inc i ## terminating '\0' character.
result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = else:
## This is the implementation to format floats in the Nim proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
## programming language. The specific format for floating point importc: "sprintf", varargs, noSideEffect.}
## numbers is not specified in the Nim programming language and
## might change slightly in the future, but at least wherever you proc writeToBuffer(buf: var array[65, char]; value: cstring) =
## format a float, it should be consistent. var i = 0
## while value[i] != '\0':
## returns the amount of bytes written to `buf` not counting the buf[i] = value[i]
## terminating '\0' character. inc i
##
## * `buf` - A buffer to write into. The buffer does not need to be proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int =
## initialized and it will be overridden. ## This is the implementation to format floats.
## ##
var n: int = c_sprintf(addr buf, "%.16g", value) ## returns the amount of bytes written to `buf` not counting the
var hasDot = false ## terminating '\0' character.
for i in 0..n-1: var n: int = c_sprintf(addr buf, "%.16g", value)
if buf[i] == ',': var hasDot = false
buf[i] = '.' for i in 0..n-1:
hasDot = true if buf[i] == ',':
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: buf[i] = '.'
hasDot = true hasDot = true
if not hasDot: elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
buf[n] = '.' hasDot = true
buf[n+1] = '0' if not hasDot:
buf[n+2] = '\0' buf[n] = '.'
result = n + 2 buf[n+1] = '0'
else: buf[n+2] = '\0'
result = n result = n + 2
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
# 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: else:
writeToBuffer(buf, "inf") result = n
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
# 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 result = 3
elif buf[n-1] == 'F':
if buf[0] == '-':
writeToBuffer(buf, "-inf")
result = 4
else:
writeToBuffer(buf, "inf")
result = 3

View file

@ -1,10 +1,10 @@
discard """ discard """
output: ''' output: '''
1e-06 : 1e-06 0.000001 : 0.000001
1e-06 : 1e-06 0.000001 : 0.000001
0.001 : 0.001 0.001 : 0.001
1e-06 : 1e-06 0.000001 : 0.000001
1e-06 : 1e-06 0.000001 : 0.000001
10.000001 : 10.000001 10.000001 : 10.000001
100.000001 : 100.000001 100.000001 : 100.000001
''' '''