mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
why ? - We already have an emit that does the same thing - The name asm itself is a bit confusing, you might think it's an alias for asm.js or something else. - The asm keyword is used differently on different compiler targets (it makes it inexpressive). - Does anyone (other than some compiler libraries) use asm instead of emit ? If yes, it's a bit strange to use asm somewhere and emit somewhere. By making the asm keyword for js target deprecated, there would be even less use of the asm keyword for js target, reducing the amount of confusion. - New users might accidentally use a non-universal approach via the asm keyword instead of emit, and then when they learn about asm, try to figure out what the differences are. see https://forum.nim-lang.org/t/10821 --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
144 lines
4.4 KiB
Nim
144 lines
4.4 KiB
Nim
#
|
|
#
|
|
# Nim's Runtime Library
|
|
# (c) Copyright 2022 Nim contributors
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module implements formatting floats as strings.
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
|
|
|
|
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 std/private/[dragonbox, schubfach]
|
|
|
|
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).int
|
|
buf[result] = '\0'
|
|
|
|
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
|
|
result = float32ToChars(buf, value, forceTrailingDotZero=true).int
|
|
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 = c_sprintf(cast[cstring](addr buf), "%.16g", value).int
|
|
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:
|
|
writeToBuffer(buf, "inf")
|
|
result = 3
|
|
|
|
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
|
|
when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
|
|
writeFloatToBufferRoundtrip(buf, value)
|
|
else:
|
|
writeFloatToBufferSprintf(buf, value)
|
|
|
|
proc addFloatRoundtrip*(result: var string; x: float | float32) =
|
|
when nimvm:
|
|
raiseAssert "unreachable"
|
|
else:
|
|
var buffer {.noinit.}: array[65, char]
|
|
let n = writeFloatToBufferRoundtrip(buffer, x)
|
|
result.addCstringN(cast[cstring](buffer[0].addr), n)
|
|
|
|
proc addFloatSprintf*(result: var string; x: float) =
|
|
when nimvm:
|
|
raiseAssert "unreachable"
|
|
else:
|
|
var buffer {.noinit.}: array[65, char]
|
|
let n = writeFloatToBufferSprintf(buffer, x)
|
|
result.addCstringN(cast[cstring](buffer[0].addr), n)
|
|
|
|
when defined(js):
|
|
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
|
|
{.emit: """
|
|
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(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
|
|
addFloatRoundtrip(result, x)
|
|
else:
|
|
addFloatSprintf(result, x)
|
|
when defined(js):
|
|
when nimvm: impl()
|
|
else:
|
|
result.add nimFloatToString(x)
|
|
else: impl()
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
func `$`*(x: float | float32): string =
|
|
## Outplace version of `addFloat`.
|
|
result.addFloat(x)
|