fixes #18134; registers formatBiggestFloat in VM (#21299)

fixes #18134; registers formatBiggestFloat in vmops

strformat supports float format in VM
This commit is contained in:
ringabout 2023-01-27 06:03:59 +08:00 • committed by GitHub
commit fc068ee06d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 61 deletions

View file

@ -36,7 +36,9 @@ from std/osproc import nil
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/syncio import std/syncio
else: else:
from std/formatfloat import addFloatRoundtrip, addFloatSprintf from std/formatfloat import addFloatRoundtrip, addFloatSprintf
from std/strutils import formatBiggestFloat, FloatFormatMode
# There are some useful procs in vmconv. # There are some useful procs in vmconv.
import vmconv, vmmarshal import vmconv, vmmarshal
@ -374,6 +376,10 @@ proc registerAdditionalOps*(c: PCtx) =
let x = a.getFloat(1) let x = a.getFloat(1)
addFloatSprintf(p.strVal, x) addFloatSprintf(p.strVal, x)
registerCallback c, "stdlib.strutils.formatBiggestFloat", proc(a: VmArgs) =
setResult(a, formatBiggestFloat(a.getFloat(0), FloatFormatMode(a.getInt(1)),
a.getInt(2), chr(a.getInt(3))))
wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs() wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs()
registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) = registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) =

View file

@ -2413,60 +2413,63 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
doAssert x.formatBiggestFloat() == "123.4560000000000" doAssert x.formatBiggestFloat() == "123.4560000000000"
doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560" doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560"
doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02" doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02"
when defined(js): when nimvm:
var precision = precision discard "implemented in the vmops"
if precision == -1:
# use the same default precision as c_sprintf
precision = 6
var res: cstring
case format
of ffDefault:
{.emit: "`res` = `f`.toString();".}
of ffDecimal:
{.emit: "`res` = `f`.toFixed(`precision`);".}
of ffScientific:
{.emit: "`res` = `f`.toExponential(`precision`);".}
result = $res
if 1.0 / f == -Inf:
# JavaScript removes the "-" from negative Zero, add it back here
result = "-" & $res
for i in 0 ..< result.len:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if result[i] in {'.', ','}: result[i] = decimalSep
else: else:
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] when defined(js):
var var precision = precision
frmtstr {.noinit.}: array[0..5, char] if precision == -1:
buf {.noinit.}: array[0..2500, char] # use the same default precision as c_sprintf
L: cint precision = 6
frmtstr[0] = '%' var res: cstring
if precision >= 0: case format
frmtstr[1] = '#' of ffDefault:
frmtstr[2] = '.' {.emit: "`res` = `f`.toString();".}
frmtstr[3] = '*' of ffDecimal:
frmtstr[4] = floatFormatToChar[format] {.emit: "`res` = `f`.toFixed(`precision`);".}
frmtstr[5] = '\0' of ffScientific:
L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), precision, f) {.emit: "`res` = `f`.toExponential(`precision`);".}
result = $res
if 1.0 / f == -Inf:
# JavaScript removes the "-" from negative Zero, add it back here
result = "-" & $res
for i in 0 ..< result.len:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if result[i] in {'.', ','}: result[i] = decimalSep
else: else:
frmtstr[1] = floatFormatToChar[format] const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
frmtstr[2] = '\0' var
L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), f) frmtstr {.noinit.}: array[0..5, char]
result = newString(L) buf {.noinit.}: array[0..2500, char]
for i in 0 ..< L: L: cint
# Depending on the locale either dot or comma is produced, frmtstr[0] = '%'
# but nothing else is possible: if precision >= 0:
if buf[i] in {'.', ','}: result[i] = decimalSep frmtstr[1] = '#'
else: result[i] = buf[i] frmtstr[2] = '.'
when defined(windows): frmtstr[3] = '*'
# VS pre 2015 violates the C standard: "The exponent always contains at frmtstr[4] = floatFormatToChar[format]
# least two digits, and only as many more digits as necessary to frmtstr[5] = '\0'
# represent the exponent." [C11 §7.21.6.1] L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), precision, f)
# The following post-processing fixes this behavior. else:
if result.len > 4 and result[^4] == '+' and result[^3] == '0': frmtstr[1] = floatFormatToChar[format]
result[^3] = result[^2] frmtstr[2] = '\0'
result[^2] = result[^1] L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), f)
result.setLen(result.len - 1) result = newString(L)
for i in 0 ..< L:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalSep
else: result[i] = buf[i]
when defined(windows):
# VS pre 2015 violates the C standard: "The exponent always contains at
# least two digits, and only as many more digits as necessary to
# represent the exponent." [C11 §7.21.6.1]
# The following post-processing fixes this behavior.
if result.len > 4 and result[^4] == '+' and result[^3] == '0':
result[^3] = result[^2]
result[^2] = result[^1]
result.setLen(result.len - 1)
func formatFloat*(f: float, format: FloatFormatMode = ffDefault, func formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[-1..32] = 16; decimalSep = '.'): string {. precision: range[-1..32] = 16; decimalSep = '.'): string {.

View file

@ -475,15 +475,17 @@ proc main() =
# Note: times.format adheres to the format protocol. Test that this # Note: times.format adheres to the format protocol. Test that this
# works: # works:
when nimvm:
discard
else:
var dt = dateTime(2000, mJan, 01, 00, 00, 00)
check &"{dt:yyyy-MM-dd}", "2000-01-01"
var dt = initDateTime(01, mJan, 2000, 00, 00, 00) var tm = fromUnix(0)
check &"{dt:yyyy-MM-dd}", "2000-01-01" discard &"{tm}"
var tm = fromUnix(0) var noww = now()
discard &"{tm}" check &"{noww}", $noww
var noww = now()
check &"{noww}", $noww
# Unicode string tests # Unicode string tests
check &"""{"αβγ"}""", "αβγ" check &"""{"αβγ"}""", "αβγ"
@ -558,5 +560,6 @@ proc main() =
doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')"
doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')"
doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')"
# xxx static: main()
static: main()
main() main()