summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-07-20 13:13:52 -0700
committerGitHub <noreply@github.com>2021-07-20 22:13:52 +0200
commitcf0cf32d276002e850a87667fff62c4df12999d6 (patch)
treed35564ef08d681941158d7d457d797e9775b40eb /lib
parenta8b3e7c05919511db62f1aabd706c46316b4f7b6 (diff)
downloadNim-cf0cf32d276002e850a87667fff62c4df12999d6.tar.gz
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
Diffstat (limited to 'lib')
-rw-r--r--lib/system.nim3
-rw-r--r--lib/system/dollars.nim13
-rw-r--r--lib/system/formatfloat.nim166
-rw-r--r--lib/system/jssys.nim18
-rw-r--r--lib/system/repr_v2.nim6
-rw-r--r--lib/system/strmantle.nim41
6 files changed, 126 insertions, 121 deletions
diff --git a/lib/system.nim b/lib/system.nim
index e878f9f2f..1da1444d2 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -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"):
diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim
index baae8f090..8634db382 100644
--- a/lib/system/dollars.nim
+++ b/lib/system/dollars.nim
@@ -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`
diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim
index 772f0a848..cb46c6ea7 100644
--- a/lib/system/formatfloat.nim
+++ b/lib/system/formatfloat.nim
@@ -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()
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 7cb5652c5..b42dc3a20 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -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
diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim
index 618cc2b40..8471ea148 100644
--- a/lib/system/repr_v2.nim
+++ b/lib/system/repr_v2.nim
@@ -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`
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 041272175..81ac5c751 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -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.}