Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix math.frexp function signature #16725

Merged
merged 19 commits into from
Feb 17, 2021
Merged
5 changes: 3 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down Expand Up @@ -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`,
Expand Down
88 changes: 52 additions & 36 deletions lib/pure/math.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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>".}
ringabout marked this conversation as resolved.
Show resolved Hide resolved

func binom*(n, k: int): int =
## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
runnableExamples:
Expand Down Expand Up @@ -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>".}
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
func c_frexp*(x: float64, exponent: var int32): float64 {.
importc: "frexp", header: "<math.h>".}
func frexp*[T, U](x: T, exponent: var U): T =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## 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 a number into mantissa and exponent.
##
## `frexp` calculates the mantissa `frac` (a float with an absolute value
## greater than or equal to 0.5 and less than 1) and the integer value
## `exp` such that `x` (the original float value) equals `frac * 2 ^ exp`.
ringabout marked this conversation as resolved.
Show resolved Hide resolved
runnableExamples:
doAssert frexp(8.0) == (0.5, 4)
doAssert frexp(-8.0) == (-0.5, 4)
doAssert frexp(0.0) == (0.0, 0)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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

since (1, 5, 1):
export frexp
ringabout marked this conversation as resolved.
Show resolved Hide resolved

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
Expand Down Expand Up @@ -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.
##
Expand Down
13 changes: 10 additions & 3 deletions tests/stdlib/tfrexp1.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
discard """
targets: "js c cpp"
output: '''ok'''
"""

import math
import std/math

const manualTest = false

Expand Down Expand Up @@ -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