make -d:nimFpRoundtrips work consistently in vm vs rt, fix #18400, etc (#18531)

* compiler/vmhooks: add getVar to allow vmops with var params
* addFloat vmops with var param
* cgen now renders float32 literals in c backend using roundtrip float to string
This commit is contained in:
Timothee Cour
2021-07-20 13:13:52 -07:00
committed by GitHub
parent a8b3e7c059
commit cf0cf32d27
21 changed files with 286 additions and 199 deletions

View File

@@ -652,7 +652,7 @@ type
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
mUnaryPlusI, mBitnotI,
mUnaryPlusF64, mUnaryMinusF64,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
mImplies, mIff, mExists, mForall, mOld,
@@ -720,7 +720,7 @@ const
mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
mUnaryPlusF64, mUnaryMinusF64,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,

View File

@@ -94,9 +94,12 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
else:
result = makeCString(n.strVal)
of nkFloatLit, nkFloat64Lit:
result = rope(n.floatVal.toStrMaxPrecision)
if ty.kind == tyFloat32:
result = rope(n.floatVal.float32.toStrMaxPrecision)
else:
result = rope(n.floatVal.toStrMaxPrecision)
of nkFloat32Lit:
result = rope(n.floatVal.toStrMaxPrecision("f"))
result = rope(n.floatVal.float32.toStrMaxPrecision)
else:
internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
result = nil
@@ -2300,11 +2303,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)")
of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)")
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
of mFloatToStr:
if e[1].typ.skipTypes(abstractInst).kind == tyFloat32:
genDollar(p, e, d, "#nimFloat32ToStr($1)")
else:
genDollar(p, e, d, "#nimFloatToStr($1)")
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr, mUnown: expr(p, e[1], d)
of mIsolate: genCall(p, e, d)

View File

@@ -434,7 +434,6 @@ const # magic checked op; magic unchecked op;
mBoolToStr: ["nimBoolToStr", "nimBoolToStr"],
mIntToStr: ["cstrToNimstr", "cstrToNimstr"],
mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"],
mFloatToStr: ["cstrToNimstr", "cstrToNimstr"],
mCStrToStr: ["cstrToNimstr", "cstrToNimstr"],
mStrToStr: ["", ""]]
@@ -656,9 +655,6 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
of mFloatToStr:
useMagic(p, "nimFloatToString")
applyFormat "cstrToNimstr(nimFloatToString($1))"
of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1")
else:
@@ -682,8 +678,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
gen(p, n[1], x)
gen(p, n[2], y)
r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc]
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
mCStrToStr, mStrToStr, mEnumToStr:
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
arithAux(p, n, r, op)
of mEqRef:
if mapType(n[1].typ) != etyBaseIndex:

View File

@@ -4,6 +4,7 @@ hint:XDeclaredButNotUsed:off
define:booting
define:nimcore
define:nimFpRoundtrips
#import:"$projectpath/testability"

View File

@@ -39,7 +39,10 @@ when not declared(signbit):
proc signbit*(x: SomeFloat): bool {.inline.} =
result = c_signbit(x) != 0
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
import system/formatfloat
proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
const literalPostfix = when f is float32: "f" else: ""
case classify(f)
of fcNan:
if signbit(f):
@@ -55,9 +58,8 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
of fcNegInf:
result = "-INF"
else:
result = newString(81)
let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
setLen(result, n)
result.addFloatRoundtrip(f)
result.add literalPostfix
proc encodeStr*(s: string, result: var string) =
for i in 0..<s.len:

View File

@@ -290,7 +290,6 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
else: result = newStrNodeT("true", n, g)
of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
of mCStrToStr, mCharToStr:
if a.kind == nkBracket:
var s = ""

View File

@@ -1127,8 +1127,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8):
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
genConv(c, n, n[1], dest)
of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)

View File

@@ -36,10 +36,14 @@ proc setResult*(a: VmArgs; v: seq[string]) =
for x in v: n.add newStrNode(nkStrLit, x)
a.slots[a.ra].node = n
template getX(k, field) {.dirty.} =
template getReg(a, i): untyped =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == k
result = a.slots[i+a.rb+1].field
a.slots[i+a.rb+1].unsafeAddr
template getX(k, field): untyped {.dirty.} =
let p = getReg(a, i)
doAssert p.kind == k, $p.kind
p.field
proc numArgs*(a: VmArgs): int =
result = a.rc-1
@@ -47,19 +51,17 @@ proc numArgs*(a: VmArgs): int =
proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0
proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
proc getString*(a: VmArgs; i: Natural): string =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNode
result = a.slots[i+a.rb+1].node.strVal
proc getNode*(a: VmArgs; i: Natural): PNode =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNode
result = a.slots[i+a.rb+1].node
proc getNode*(a: VmArgs; i: Natural): PNode = getX(rkNode, node)
proc getString*(a: VmArgs; i: Natural): string = getX(rkNode, node).strVal
proc getVar*(a: VmArgs; i: Natural): PNode =
let p = getReg(a, i)
# depending on whether we come from top-level or proc scope, we need to consider 2 cases
case p.kind
of rkRegisterAddr: result = p.regAddr.node
of rkNodeAddr: result = p.nodeAddr[]
else: doAssert false, $p.kind
proc getNodeAddr*(a: VmArgs; i: Natural): PNode =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNodeAddr
let nodeAddr = a.slots[i+a.rb+1].nodeAddr
let nodeAddr = getX(rkNodeAddr, nodeAddr)
doAssert nodeAddr != nil
result = nodeAddr[]

View File

@@ -27,6 +27,7 @@ from std/md5 import getMD5
from std/times import cpuTime
from std/hashes import hash
from std/osproc import nil
from system/formatfloat import addFloatRoundtrip, addFloatSprintf
from sighashes import symBodyDigest
@@ -325,3 +326,13 @@ proc registerAdditionalOps*(c: PCtx) =
registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
let fn = getNode(a, 0)
setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure))
registerCallback c, "stdlib.formatfloat.addFloatRoundtrip", proc(a: VmArgs) =
let p = a.getVar(0)
let x = a.getFloat(1)
addFloatRoundtrip(p.strVal, x)
registerCallback c, "stdlib.formatfloat.addFloatSprintf", proc(a: VmArgs) =
let p = a.getVar(0)
let x = a.getFloat(1)
addFloatSprintf(p.strVal, x)

View File

@@ -2472,9 +2472,6 @@ when defined(js) or defined(nimscript):
proc addInt*(result: var string; x: int64) =
result.add $x
proc addFloat*(result: var string; x: float) =
result.add $x
proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
## A shorthand for `echo(errormsg); quit(errorcode)`.
when defined(nimscript) or defined(js) or (hostOS == "standalone"):

View File

@@ -1,5 +1,6 @@
import std/private/digitsutils
import system/formatfloat
export addFloat
proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.}
## The stringify operator for an integer argument. Returns `x`
@@ -40,13 +41,9 @@ proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.}
## The stringify operator for an integer argument. Returns `x`
## converted to a decimal string.
proc `$`*(x: float): string {.magic: "FloatToStr", noSideEffect.}
## The stringify operator for a float argument. Returns `x`
## converted to a decimal string.
proc `$`*(x: float32): string {.magic: "FloatToStr", noSideEffect.}
## The stringify operator for a float32 argument. Returns `x`
## converted to a decimal string.
func `$`*(x: float | float32): string =
## Outplace version of `addFloat`.
result.addFloat(x)
proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
## The stringify operator for a boolean argument. Returns `x`

View File

@@ -7,58 +7,126 @@
# distribution, for details about the copyright.
#
when defined(nimFpRoundtrips) and not defined(nimscript) and
not defined(js) and defined(nimHasDragonBox):
import dragonbox
proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
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
c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
else:
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.}
import dragonbox, schubfach
proc writeToBuffer(buf: var array[65, char]; value: cstring) =
var i = 0
while value[i] != '\0':
buf[i] = value[i]
inc i
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
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
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
result = float32ToChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
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 writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
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' 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:
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")
writeToBuffer(buf, "inf")
result = 3
elif buf[n-1] == 'F':
if buf[0] == '-':
writeToBuffer(buf, "-inf")
result = 4
else:
writeToBuffer(buf, "inf")
result = 3
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
when defined(nimFpRoundtrips):
writeFloatToBufferRoundtrip(buf, value)
else:
writeFloatToBufferSprintf(buf, value)
proc addFloatRoundtrip*(result: var string; x: float | float32) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferRoundtrip(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc addFloatSprintf*(result: var string; x: float) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferSprintf(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc nimFloatToString(a: float): cstring =
## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
# print `-0.0` properly
asm """
function nimOnlyDigitsOrMinus(n) {
return n.toString().match(/^-?\d+$/);
}
if (Number.isSafeInteger(`a`))
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
else {
`result` = `a`+""
if(nimOnlyDigitsOrMinus(`result`)){
`result` = `a`+".0"
}
}
"""
proc addFloat*(result: var string; x: float | float32) {.inline.} =
## Converts float to its string representation and appends it to `result`.
runnableExamples:
var
s = "foo:"
b = 45.67
s.addFloat(45.67)
assert s == "foo:45.67"
template impl =
when defined(nimFpRoundtrips):
addFloatRoundtrip(result, x)
else:
addFloatSprintf(result, x)
when defined(js):
when nimvm: impl()
else:
result.add nimFloatToString(x)
else: impl()

View File

@@ -496,23 +496,6 @@ proc negInt(a: int): int {.compilerproc.} =
proc negInt64(a: int64): int64 {.compilerproc.} =
result = a*(-1)
proc nimFloatToString(a: float): cstring {.compilerproc.} =
## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
# print `-0.0` properly
asm """
function nimOnlyDigitsOrMinus(n) {
return n.toString().match(/^-?\d+$/);
}
if (Number.isSafeInteger(`a`))
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
else {
`result` = `a`+""
if(nimOnlyDigitsOrMinus(`result`)){
`result` = `a`+".0"
}
}
"""
proc absInt(a: int): int {.compilerproc.} =
result = if a < 0: a*(-1) else: a
@@ -703,6 +686,7 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
{.pop.}
proc tenToThePowerOf(b: int): BiggestFloat =
# xxx deadcode
var b = b
var a = 10.0
result = 1.0

View File

@@ -19,9 +19,9 @@ proc repr*(x: uint64): string {.noSideEffect.} =
## converted to a decimal string.
$x #Calls `$` from system/strmantle.nim
proc repr*(x: float): string {.magic: "FloatToStr", noSideEffect.}
## repr for a float argument. Returns `x`
## converted to a decimal string.
proc repr*(x: float): string =
## Same as $x
$x
proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
## repr for a boolean argument. Returns `x`

View File

@@ -69,47 +69,6 @@ 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`.
##
## .. code-block:: Nim
## var
## a = "123"
## b = 45.67
## a.addFloat(b) # a <- "12345.67"
when nimvm:
result.add $x
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBuffer(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc nimFloatToStr(f: float): string {.compilerproc.} =
result = newStringOfCap(8)
result.addFloat f
when defined(nimFpRoundtrips) and not defined(nimscript) and
not defined(js) and defined(nimHasDragonBox):
import schubfach
proc nimFloat32ToStr(f: float32): string {.compilerproc.} =
when declared(float32ToChars):
result = newString(65)
let L = float32ToChars(result, f, forceTrailingDotZero=true)
setLen(result, L)
else:
result = newStringOfCap(8)
result.addFloat f
proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
importc: "strtod", header: "<stdlib.h>", noSideEffect.}

View File

@@ -34,3 +34,5 @@ hint("Processing", off)
switch("define", "nimExperimentalAsyncjsThen")
switch("define", "nimExperimentalJsfetch")
switch("define", "nimExperimentalLinenoiseExtra")
switch("define", "nimFpRoundtrips")

View File

@@ -13,12 +13,12 @@ treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUse
treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed]
treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed]
treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed]
treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed]
treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed]
'''
action: compile
"""
#treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed]
#treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed]
# bug #9764
iterator s1(a:string): int = discard
iterator s2(): int = discard
@@ -32,9 +32,7 @@ var s9: int
type s10 = object
type s11 = type(1.2)
when false:
# enabled again when Nim bootstraps with -d:nimFpRoundtrips
# https://github.com/nim-lang/Nim/issues/14407
let
`v0.99` = "0.99"
`v0.99.99` = "0.99.99"
# bug #14407 (requires `compiler/nim.cfg` containing define:nimFpRoundtrips)
let
`v0.99` = "0.99"
`v0.99.99` = "0.99.99"

View File

@@ -1 +0,0 @@
-d:nimFpRoundtrips

View File

@@ -1,13 +1,14 @@
discard """
matrix: "-d:nimFpRoundtrips; -u:nimFpRoundtrips"
targets: "c cpp js"
"""
# disabled: "windows"
#[
xxx merge all or most float tests into this file
]#
import std/[fenv, math, strutils]
import stdtest/testutils
proc equalsOrNaNs(a, b: float): bool =
if isNaN(a): isNaN(b)
@@ -62,27 +63,103 @@ template main =
reject "1_.0"
reject "1.0_"
block: # bug #18148
var a = 1.1'f32
doAssert $a == "1.1", $a # was failing
block: # bugs mentioned in https://github.com/nim-lang/Nim/pull/18504#issuecomment-881635317
block: # example 1
let a = 0.1+0.2
doAssert a != 0.3
when defined(nimFpRoundtrips):
doAssert $a == "0.30000000000000004"
else:
whenRuntimeJs: discard
do: doAssert $a == "0.3"
block: # example 2
const a = 0.1+0.2
when defined(nimFpRoundtrips):
doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)"""
else:
whenRuntimeJs: discard
do: doAssert $($a, a) == """("0.3", 0.3)"""
block: # example 3
const a1 = 0.1+0.2
let a2 = a1
doAssert a1 != 0.3
when defined(nimFpRoundtrips):
doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]"""
else:
whenRuntimeJs: discard
do: doAssert $[$a1, $a2] == """["0.3", "0.3"]"""
proc runtimeOnlyTests =
# enable for 'static' once -d:nimFpRoundtrips became the default
block: # bug #7717
proc test(f: float) =
let f2 = $f
let f3 = parseFloat(f2)
doAssert equalsOrNaNs(f, f3), $(f, f2, f3)
when defined(nimFpRoundtrips):
block: # bug #18148
var a = 1.1'f32
doAssert $a == "1.1", $a # was failing
test 1.0 + epsilon(float64)
test 1000000.0000000123
test log2(100000.0)
test maximumPositiveValue(float32)
test maximumPositiveValue(float64)
test minimumPositiveValue(float32)
test minimumPositiveValue(float64)
block: # bug #18400
block:
let a1 = 0.1'f32
let a2 = 0.2'f32
let a3 = a1 + a2
var s = ""
s.addFloat(a3)
whenVMorJs: discard # xxx refs #12884
do:
doAssert a3 == 0.3'f32
doAssert $a3 == "0.3"
block:
let a1 = 0.1
let a2 = 0.2
let a3 = a1 + a2
var s = ""
s.addFloat(a3)
doAssert a3 != 0.3
doAssert $a3 == "0.30000000000000004"
block:
var s = [-13.888888'f32]
whenRuntimeJs: discard
do:
doAssert $s == "[-13.888888]"
doAssert $s[0] == "-13.888888"
block: # bug #7717
proc test(f: float) =
let f2 = $f
let f3 = parseFloat(f2)
doAssert equalsOrNaNs(f, f3), $(f, f2, f3)
test 1.0 + epsilon(float64)
test 1000000.0000000123
test log2(100000.0)
test maximumPositiveValue(float32)
test maximumPositiveValue(float64)
test minimumPositiveValue(float32)
test minimumPositiveValue(float64)
block: # bug #12884
block: # example 1
const x0: float32 = 1.32
let x1 = 1.32
let x2 = 1.32'f32
var x3: float32 = 1.32
doAssert $(x0, x1, x2, x3) == "(1.32, 1.32, 1.32, 1.32)"
block: # example https://github.com/nim-lang/Nim/issues/12884#issuecomment-564967962
let x = float(1.32'f32)
when nimvm: discard # xxx prints 1.3
else:
when not defined(js):
doAssert $x == "1.3200000524520874"
doAssert $1.32 == "1.32"
doAssert $1.32'f32 == "1.32"
let x2 = 1.32'f32
doAssert $x2 == "1.32"
block:
var x = 1.23456789012345'f32
when nimvm:
discard # xxx, refs #12884
else:
when not defined(js):
doAssert x == 1.2345679'f32
doAssert $x == "1.2345679"
static: main()
main()
runtimeOnlyTests()

View File

@@ -303,7 +303,7 @@ let jsonNode = %*mynode
doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}"""
doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])"""
when defined(nimFpRoundtrips): # bug #17383
block: # bug #17383
testRoundtrip(int32.high): "2147483647"
testRoundtrip(uint32.high): "4294967295"
when int.sizeof == 4:
@@ -316,7 +316,7 @@ when defined(nimFpRoundtrips): # bug #17383
testRoundtrip(int64.high): "9223372036854775807"
testRoundtrip(uint64.high): "18446744073709551615"
when defined(nimFpRoundtrips): # bug #18007
block: # bug #18007
testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]"""
# pending https://github.com/nim-lang/Nim/issues/18025 use:
# testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0])
@@ -332,7 +332,7 @@ when defined(nimFpRoundtrips): # bug #18007
testRoundtripVal(0.0): "0.0"
testRoundtripVal(-0.0): "-0.0"
when defined(nimFpRoundtrips): # bug #15397, bug #13196
block: # bug #15397, bug #13196
testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"

View File

@@ -161,16 +161,15 @@ template fn() =
doAssert b[2].signbit
doAssert not b[3].signbit
when defined(nimFpRoundtrips):
block: # bug #15397, bug #13196
let a = 0.1
let x = 0.12345678901234567890123456789
let b = (a + 0.2, 0.3, x)
testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]"
block: # bug #15397, bug #13196
let a = 0.1
let x = 0.12345678901234567890123456789
let b = (a + 0.2, 0.3, x)
testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]"
testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"
testRoundtripVal(epsilon(float64)): "2.220446049250313e-16"
testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"
testRoundtripVal(epsilon(float64)): "2.220446049250313e-16"
testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
block: # case object
type Foo = object