* 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:
parent
a8b3e7c059
commit
cf0cf32d27
21 changed files with 286 additions and 199 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ hint:XDeclaredButNotUsed:off
|
||||||
|
|
||||||
define:booting
|
define:booting
|
||||||
define:nimcore
|
define:nimcore
|
||||||
|
define:nimFpRoundtrips
|
||||||
|
|
||||||
#import:"$projectpath/testability"
|
#import:"$projectpath/testability"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 = ""
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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[]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"):
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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,7 +26,10 @@ 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 =
|
||||||
|
result = float32ToChars(buf, value, forceTrailingDotZero=true)
|
||||||
|
buf[result] = '\0'
|
||||||
|
|
||||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||||
importc: "sprintf", varargs, noSideEffect.}
|
importc: "sprintf", varargs, noSideEffect.}
|
||||||
|
|
||||||
|
|
@ -29,7 +39,7 @@ else:
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
# https://github.com/nim-lang/Nim/issues/14407
|
|
||||||
let
|
let
|
||||||
`v0.99` = "0.99"
|
`v0.99` = "0.99"
|
||||||
`v0.99.99` = "0.99.99"
|
`v0.99.99` = "0.99.99"
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
-d:nimFpRoundtrips
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue