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
commit cf0cf32d27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 286 additions and 199 deletions

View file

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

View file

@ -94,9 +94,12 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
else: else:
result = makeCString(n.strVal) result = makeCString(n.strVal)
of nkFloatLit, nkFloat64Lit: of nkFloatLit, nkFloat64Lit:
if ty.kind == tyFloat32:
result = rope(n.floatVal.float32.toStrMaxPrecision)
else:
result = rope(n.floatVal.toStrMaxPrecision) result = rope(n.floatVal.toStrMaxPrecision)
of nkFloat32Lit: of nkFloat32Lit:
result = rope(n.floatVal.toStrMaxPrecision("f")) result = rope(n.floatVal.float32.toStrMaxPrecision)
else: else:
internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
result = nil 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 mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)")
of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)")
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($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 mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr, mUnown: expr(p, e[1], d) of mStrToStr, mUnown: expr(p, e[1], d)
of mIsolate: genCall(p, e, d) of mIsolate: genCall(p, e, d)

View file

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

View file

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

View file

@ -39,7 +39,10 @@ when not declared(signbit):
proc signbit*(x: SomeFloat): bool {.inline.} = proc signbit*(x: SomeFloat): bool {.inline.} =
result = c_signbit(x) != 0 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) case classify(f)
of fcNan: of fcNan:
if signbit(f): if signbit(f):
@ -55,9 +58,8 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
of fcNegInf: of fcNegInf:
result = "-INF" result = "-INF"
else: else:
result = newString(81) result.addFloatRoundtrip(f)
let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring) result.add literalPostfix
setLen(result, n)
proc encodeStr*(s: string, result: var string) = proc encodeStr*(s: string, result: var string) =
for i in 0..<s.len: 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: of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n, g) if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
else: result = newStrNodeT("true", n, g) else: result = newStrNodeT("true", n, g)
of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
of mCStrToStr, mCharToStr: of mCStrToStr, mCharToStr:
if a.kind == nkBracket: if a.kind == nkBracket:
var s = "" 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}) let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8):
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
genConv(c, n, n[1], dest) genConv(c, n, n[1], dest)
of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
of mLeStr: genBinaryABC(c, n, dest, opcLeStr) 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) for x in v: n.add newStrNode(nkStrLit, x)
a.slots[a.ra].node = n a.slots[a.ra].node = n
template getX(k, field) {.dirty.} = template getReg(a, i): untyped =
doAssert i < a.rc-1 doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == k a.slots[i+a.rb+1].unsafeAddr
result = a.slots[i+a.rb+1].field
template getX(k, field): untyped {.dirty.} =
let p = getReg(a, i)
doAssert p.kind == k, $p.kind
p.field
proc numArgs*(a: VmArgs): int = proc numArgs*(a: VmArgs): int =
result = a.rc-1 result = a.rc-1
@ -47,19 +51,17 @@ proc numArgs*(a: VmArgs): int =
proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal) proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0 proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0
proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal) proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
proc getString*(a: VmArgs; i: Natural): string = proc getNode*(a: VmArgs; i: Natural): PNode = getX(rkNode, node)
doAssert i < a.rc-1 proc getString*(a: VmArgs; i: Natural): string = getX(rkNode, node).strVal
doAssert a.slots[i+a.rb+1].kind == rkNode proc getVar*(a: VmArgs; i: Natural): PNode =
result = a.slots[i+a.rb+1].node.strVal let p = getReg(a, i)
# depending on whether we come from top-level or proc scope, we need to consider 2 cases
proc getNode*(a: VmArgs; i: Natural): PNode = case p.kind
doAssert i < a.rc-1 of rkRegisterAddr: result = p.regAddr.node
doAssert a.slots[i+a.rb+1].kind == rkNode of rkNodeAddr: result = p.nodeAddr[]
result = a.slots[i+a.rb+1].node else: doAssert false, $p.kind
proc getNodeAddr*(a: VmArgs; i: Natural): PNode = proc getNodeAddr*(a: VmArgs; i: Natural): PNode =
doAssert i < a.rc-1 let nodeAddr = getX(rkNodeAddr, nodeAddr)
doAssert a.slots[i+a.rb+1].kind == rkNodeAddr
let nodeAddr = a.slots[i+a.rb+1].nodeAddr
doAssert nodeAddr != nil doAssert nodeAddr != nil
result = nodeAddr[] result = nodeAddr[]

View file

@ -27,6 +27,7 @@ from std/md5 import getMD5
from std/times import cpuTime from std/times import cpuTime
from std/hashes import hash from std/hashes import hash
from std/osproc import nil from std/osproc import nil
from system/formatfloat import addFloatRoundtrip, addFloatSprintf
from sighashes import symBodyDigest from sighashes import symBodyDigest
@ -325,3 +326,13 @@ proc registerAdditionalOps*(c: PCtx) =
registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) = registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
let fn = getNode(a, 0) let fn = getNode(a, 0)
setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure)) 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) = proc addInt*(result: var string; x: int64) =
result.add $x result.add $x
proc addFloat*(result: var string; x: float) =
result.add $x
proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
## A shorthand for `echo(errormsg); quit(errorcode)`. ## A shorthand for `echo(errormsg); quit(errorcode)`.
when defined(nimscript) or defined(js) or (hostOS == "standalone"): when defined(nimscript) or defined(js) or (hostOS == "standalone"):

View file

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

View file

@ -7,11 +7,18 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
when defined(nimFpRoundtrips) and not defined(nimscript) and proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
not defined(js) and defined(nimHasDragonBox):
import dragonbox
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = 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)
import dragonbox, schubfach
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats. ## This is the implementation to format floats.
## ##
## returns the amount of bytes written to `buf` not counting the ## returns the amount of bytes written to `buf` not counting the
@ -19,17 +26,20 @@ when defined(nimFpRoundtrips) and not defined(nimscript) and
result = toChars(buf, value, forceTrailingDotZero=true) result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0' buf[result] = '\0'
else: proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", result = float32ToChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.} importc: "sprintf", varargs, noSideEffect.}
proc writeToBuffer(buf: var array[65, char]; value: cstring) = proc writeToBuffer(buf: var array[65, char]; value: cstring) =
var i = 0 var i = 0
while value[i] != '\0': while value[i] != '\0':
buf[i] = value[i] buf[i] = value[i]
inc i inc i
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats. ## This is the implementation to format floats.
## ##
## returns the amount of bytes written to `buf` not counting the ## returns the amount of bytes written to `buf` not counting the
@ -62,3 +72,61 @@ else:
else: else:
writeToBuffer(buf, "inf") writeToBuffer(buf, "inf")
result = 3 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.} = proc negInt64(a: int64): int64 {.compilerproc.} =
result = a*(-1) 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.} = proc absInt(a: int): int {.compilerproc.} =
result = if a < 0: a*(-1) else: a result = if a < 0: a*(-1) else: a
@ -703,6 +686,7 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
{.pop.} {.pop.}
proc tenToThePowerOf(b: int): BiggestFloat = proc tenToThePowerOf(b: int): BiggestFloat =
# xxx deadcode
var b = b var b = b
var a = 10.0 var a = 10.0
result = 1.0 result = 1.0

View file

@ -19,9 +19,9 @@ proc repr*(x: uint64): string {.noSideEffect.} =
## converted to a decimal string. ## converted to a decimal string.
$x #Calls `$` from system/strmantle.nim $x #Calls `$` from system/strmantle.nim
proc repr*(x: float): string {.magic: "FloatToStr", noSideEffect.} proc repr*(x: float): string =
## repr for a float argument. Returns `x` ## Same as $x
## converted to a decimal string. $x
proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.} proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
## repr for a boolean argument. Returns `x` ## 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 = 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) =
## 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 {. proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
importc: "strtod", header: "<stdlib.h>", noSideEffect.} importc: "strtod", header: "<stdlib.h>", noSideEffect.}

View file

@ -34,3 +34,5 @@ hint("Processing", off)
switch("define", "nimExperimentalAsyncjsThen") switch("define", "nimExperimentalAsyncjsThen")
switch("define", "nimExperimentalJsfetch") switch("define", "nimExperimentalJsfetch")
switch("define", "nimExperimentalLinenoiseExtra") 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(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed]
treportunused.nim(32, 6) Hint: 's10' 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(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 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 # bug #9764
iterator s1(a:string): int = discard iterator s1(a:string): int = discard
iterator s2(): int = discard iterator s2(): int = discard
@ -32,9 +32,7 @@ var s9: int
type s10 = object type s10 = object
type s11 = type(1.2) type s11 = type(1.2)
when false: # bug #14407 (requires `compiler/nim.cfg` containing define:nimFpRoundtrips)
# enabled again when Nim bootstraps with -d:nimFpRoundtrips let
# https://github.com/nim-lang/Nim/issues/14407
let
`v0.99` = "0.99" `v0.99` = "0.99"
`v0.99.99` = "0.99.99" `v0.99.99` = "0.99.99"

View file

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

View file

@ -1,13 +1,14 @@
discard """ discard """
matrix: "-d:nimFpRoundtrips; -u:nimFpRoundtrips"
targets: "c cpp js" targets: "c cpp js"
""" """
# disabled: "windows"
#[ #[
xxx merge all or most float tests into this file xxx merge all or most float tests into this file
]# ]#
import std/[fenv, math, strutils] import std/[fenv, math, strutils]
import stdtest/testutils
proc equalsOrNaNs(a, b: float): bool = proc equalsOrNaNs(a, b: float): bool =
if isNaN(a): isNaN(b) if isNaN(a): isNaN(b)
@ -62,18 +63,70 @@ template main =
reject "1_.0" reject "1_.0"
reject "1.0_" reject "1.0_"
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"]"""
when defined(nimFpRoundtrips):
block: # bug #18148 block: # bug #18148
var a = 1.1'f32 var a = 1.1'f32
doAssert $a == "1.1", $a # was failing doAssert $a == "1.1", $a # was failing
proc runtimeOnlyTests = block: # bug #18400
# enable for 'static' once -d:nimFpRoundtrips became the default 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 block: # bug #7717
proc test(f: float) = proc test(f: float) =
let f2 = $f let f2 = $f
let f3 = parseFloat(f2) let f3 = parseFloat(f2)
doAssert equalsOrNaNs(f, f3), $(f, f2, f3) doAssert equalsOrNaNs(f, f3), $(f, f2, f3)
test 1.0 + epsilon(float64) test 1.0 + epsilon(float64)
test 1000000.0000000123 test 1000000.0000000123
test log2(100000.0) test log2(100000.0)
@ -82,7 +135,31 @@ proc runtimeOnlyTests =
test minimumPositiveValue(float32) test minimumPositiveValue(float32)
test minimumPositiveValue(float64) 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() static: main()
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 == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}"""
doAssert $jsonNode.to(ContentNode) == """(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(int32.high): "2147483647"
testRoundtrip(uint32.high): "4294967295" testRoundtrip(uint32.high): "4294967295"
when int.sizeof == 4: when int.sizeof == 4:
@ -316,7 +316,7 @@ when defined(nimFpRoundtrips): # bug #17383
testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(int64.high): "9223372036854775807"
testRoundtrip(uint64.high): "18446744073709551615" 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]""" 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: # pending https://github.com/nim-lang/Nim/issues/18025 use:
# testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) # 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"
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(1.0 + epsilon(float64)): "1.0000000000000002"
testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"

View file

@ -161,7 +161,6 @@ template fn() =
doAssert b[2].signbit doAssert b[2].signbit
doAssert not b[3].signbit doAssert not b[3].signbit
when defined(nimFpRoundtrips):
block: # bug #15397, bug #13196 block: # bug #15397, bug #13196
let a = 0.1 let a = 0.1
let x = 0.12345678901234567890123456789 let x = 0.12345678901234567890123456789