summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorflywind <43030857+xflywind@users.noreply.github.com>2021-02-17 12:27:48 -0600
committerGitHub <noreply@github.com>2021-02-17 19:27:48 +0100
commit35e14998ec97deb4efdbec9390b607c876a7a17f (patch)
tree4c2845305cc1b2b8709c23d036e7574bfce627b1
parent8d63f7b4831ccdadc3f119526b5b82db064aade7 (diff)
downloadNim-35e14998ec97deb4efdbec9390b607c876a7a17f.tar.gz
fix math.frexp function signature (#16725)
-rw-r--r--changelog.md5
-rw-r--r--lib/pure/math.nim88
-rw-r--r--tests/stdlib/tfrexp1.nim13
3 files changed, 65 insertions, 41 deletions
diff --git a/changelog.md b/changelog.md
index b1dff53ba..80b9919d0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -113,10 +113,9 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.
 
 - Added `sugar.dumpToString` which improves on `sugar.dump`.
 
-
-
 - Added `math.signbit`.
 
+
 - Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably)
 - In `lists`: renamed `append` to `add` and retained `append` as an alias;
   added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
@@ -147,6 +146,8 @@ provided by the operating system.
   issues like https://github.com/nim-lang/Nim/issues/13063 (which affected error messages)
   for modules importing `std/wrapnils`.
 
+- Added `math.frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
+
 - `parseopt.initOptParser` has been made available and `parseopt` has been
   added back to `prelude` for all backends. Previously `initOptParser` was
   unavailable if the `os` module did not have `paramCount` or `paramStr`,
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 2be2b68d2..577fa47a5 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -69,6 +69,17 @@ when defined(c) or defined(cpp):
 
   proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
 
+  func c_frexp*(x: cfloat, exponent: var cint): cfloat {.
+      importc: "frexpf", header: "<math.h>", deprecated: "Use `frexp` instead".}
+  func c_frexp*(x: cdouble, exponent: var cint): cdouble {.
+      importc: "frexp", header: "<math.h>", deprecated: "Use `frexp` instead".}
+
+  # don't export `c_frexp` in the future and remove `c_frexp2`.
+  func c_frexp2(x: cfloat, exponent: var cint): cfloat {.
+      importc: "frexpf", header: "<math.h>".}
+  func c_frexp2(x: cdouble, exponent: var cint): cdouble {.
+      importc: "frexp", header: "<math.h>".}
+
 func binom*(n, k: int): int =
   ## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
   runnableExamples:
@@ -915,27 +926,49 @@ func euclMod*[T: SomeNumber](x, y: T): T {.since: (1, 5, 1).} =
   if result < 0:
     result += abs(y)
 
-when not defined(js):
-  func c_frexp*(x: float32, exponent: var int32): float32 {.
-      importc: "frexp", header: "<math.h>".}
-  func c_frexp*(x: float64, exponent: var int32): float64 {.
-      importc: "frexp", header: "<math.h>".}
-  func frexp*[T, U](x: T, exponent: var U): T =
-    ## Split a number into mantissa and exponent.
-    ##
-    ## `frexp` calculates the mantissa `m` (a float greater than or equal to 0.5
-    ## and less than 1) and the integer value `n` such that `x` (the original
-    ## float value) equals `m * 2**n`. frexp stores `n` in `exponent` and returns
-    ## `m`.
-    runnableExamples:
-      var x: int
-      doAssert frexp(5.0, x) == 0.625
-      doAssert x == 3
+func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} =
+  ## Splits `x` into a normalized fraction `frac` and an integral power of 2 `exp`,
+  ## such that `abs(frac) in 0.5..<1` and `x == frac * 2 ^ exp`, except for special
+  ## cases shown below.
+  runnableExamples:
+    doAssert frexp(8.0) == (0.5, 4)
+    doAssert frexp(-8.0) == (-0.5, 4)
+    doAssert frexp(0.0) == (0.0, 0)
+    # special cases:
+    when not defined(windows):
+      doAssert frexp(-0.0) == (-0.0, 0) # signbit preserved for +-0
+      doAssert frexp(Inf).frac == Inf # +- Inf preserved
+      doAssert frexp(NaN).frac.isNaN
+  when not defined(js):
+    var exp: cint
+    result.frac = c_frexp2(x, exp)
+    result.exp = exp
+  else:
+    if x == 0.0:
+      result = (0.0, 0)
+    elif x < 0.0:
+      result = frexp(-x)
+      result.frac = -result.frac
+    else:
+      var ex = trunc(log2(x))
+      result.exp = int(ex)
+      result.frac = x / pow(2.0, ex)
+      if abs(result.frac) >= 1:
+        inc(result.exp)
+        result.frac = result.frac / 2
+      if result.exp == 1024 and result.frac == 0.0:
+        result.frac = 0.99999999999999988898
+
+func frexp*[T: float32|float64](x: T, exponent: var int): T {.inline.} =
+  ## Overload of `frexp` that calls `(result, exponent) = frexp(x)`.
+  runnableExamples:
+    var x: int
+    doAssert frexp(5.0, x) == 0.625
+    doAssert x == 3
+  (result, exponent) = frexp(x)
 
-    var exp: int32
-    result = c_frexp(x, exp)
-    exponent = exp
 
+when not defined(js):
   when windowsCC89:
     # taken from Go-lang Math.Log2
     const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009
@@ -967,23 +1000,6 @@ when not defined(js):
         doAssert almostEqual(log2(0.0), -Inf)
         doAssert log2(-2.0).isNaN
 
-else:
-  func frexp*[T: float32|float64](x: T, exponent: var int): T =
-    if x == 0.0:
-      exponent = 0
-      result = 0.0
-    elif x < 0.0:
-      result = -frexp(-x, exponent)
-    else:
-      var ex = trunc(log2(x))
-      exponent = int(ex)
-      result = x / pow(2.0, ex)
-      if abs(result) >= 1:
-        inc(exponent)
-        result = result / 2
-      if exponent == 1024 and result == 0.0:
-        result = 0.99999999999999988898
-
 func splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## Breaks `x` into an integer and a fractional part.
   ##
diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim
index 21ecc7491..85110231d 100644
--- a/tests/stdlib/tfrexp1.nim
+++ b/tests/stdlib/tfrexp1.nim
@@ -1,9 +1,8 @@
 discard """
   targets: "js c cpp"
-  output: '''ok'''
 """
 
-import math
+import std/math
 
 const manualTest = false
 
@@ -43,4 +42,12 @@ when manualTest:
 else:
   frexp_test(-200000.0, 200000.0, 0.125)
 
-echo "ok"
+
+doAssert frexp(8.0) == (0.5, 4)
+doAssert frexp(-8.0) == (-0.5, 4)
+doAssert frexp(0.0) == (0.0, 0)
+
+block:
+  var x: int
+  doAssert frexp(5.0, x) == 0.625
+  doAssert x == 3