diff options
author | flywind <43030857+xflywind@users.noreply.github.com> | 2021-02-17 12:27:48 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-17 19:27:48 +0100 |
commit | 35e14998ec97deb4efdbec9390b607c876a7a17f (patch) | |
tree | 4c2845305cc1b2b8709c23d036e7574bfce627b1 | |
parent | 8d63f7b4831ccdadc3f119526b5b82db064aade7 (diff) | |
download | Nim-35e14998ec97deb4efdbec9390b607c876a7a17f.tar.gz |
fix math.frexp function signature (#16725)
-rw-r--r-- | changelog.md | 5 | ||||
-rw-r--r-- | lib/pure/math.nim | 88 | ||||
-rw-r--r-- | tests/stdlib/tfrexp1.nim | 13 |
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 |