Skip to content

Commit 9687aac

Browse files
github-actions[bot]Copilotdbrattliclaude
authored
feat(math): add missing math functions (log/base, modf, frexp, ldexp, remainder, isclose, nextafter, ulp, exp2, cbrt) (#255)
* feat(math): add missing math functions (log base, modf, frexp, ldexp, remainder, isclose, nextafter, ulp, exp2, cbrt) Add the following Python 3.8-3.12 math functions that were absent from the existing Math.fs bindings: - log(x, base): logarithm with explicit base - ldexp(x, i): x * 2**i - frexp(x): decompose into mantissa and exponent - modf(x): fractional and integer parts - remainder(x, y): IEEE 754 remainder (Python 3.8+) - isclose(a, b) / isclose(a, b, rel_tol, abs_tol): approximate equality - nextafter(x, y): next representable float (Python 3.9+) - ulp(x): value of least significant bit (Python 3.9+) - exp2(x): 2**x (Python 3.11+) - cbrt(x): cube root (Python 3.11+) Also adds tests for each new binding in TestMath.fs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: revert CHANGELOG.md to main, stop overwriting previous entries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: ldexp needs nativeint (Python int), cbrt test uses tolerance - ldexp's second arg must be a native Python int, not Fable's Int32 - cbrt(27.0) returns 3.0000000000000004, use isclose for comparison Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Dag Brattli <dag@brattli.net> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b13d6d2 commit 9687aac

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

‎src/stdlib/Math.fs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,25 @@ type IExports =
6969
/// Return the integer square root of the non-negative integer n
7070
/// See https://docs.python.org/3/library/math.html#math.isqrt
7171
abstract isqrt: n: int -> int
72+
/// Return x * (2**i) accurately
73+
/// See https://docs.python.org/3/library/math.html#math.ldexp
74+
abstract ldexp: x: float * i: nativeint -> float
75+
/// Return the mantissa and exponent of x as the pair (m, e)
76+
/// See https://docs.python.org/3/library/math.html#math.frexp
77+
abstract frexp: x: float -> float * int
78+
/// Return the fractional and integer parts of x
79+
/// See https://docs.python.org/3/library/math.html#math.modf
80+
abstract modf: x: float -> float * float
81+
/// Return IEEE 754-style remainder of x with respect to y (Python 3.8+)
82+
/// See https://docs.python.org/3/library/math.html#math.remainder
83+
abstract remainder: x: float * y: float -> float
84+
/// Return True if the values a and b are close to each other
85+
/// See https://docs.python.org/3/library/math.html#math.isclose
86+
abstract isclose: a: float * b: float -> bool
87+
/// Return True if the values a and b are close to each other with custom tolerances
88+
/// See https://docs.python.org/3/library/math.html#math.isclose
89+
[<NamedParams(fromIndex = 2)>]
90+
abstract isclose: a: float * b: float * ?rel_tol: float * ?abs_tol: float -> bool
7291
/// Return the least common multiple of the integers
7392
/// See https://docs.python.org/3/library/math.html#math.lcm
7493
abstract lcm: [<ParamArray>] ints: int[] -> int
@@ -107,6 +126,12 @@ type IExports =
107126
/// Check if x is a NaN (not a number)
108127
/// See https://docs.python.org/3/library/math.html#math.isnan
109128
abstract isnan: x: int -> bool
129+
/// Return the next floating-point value after x towards y (Python 3.9+)
130+
/// See https://docs.python.org/3/library/math.html#math.nextafter
131+
abstract nextafter: x: float * y: float -> float
132+
/// Return the value of the least significant bit of the float x (Python 3.9+)
133+
/// See https://docs.python.org/3/library/math.html#math.ulp
134+
abstract ulp: x: float -> float
110135

111136
// ========================================================================
112137
// Power and logarithmic functions
@@ -121,6 +146,9 @@ type IExports =
121146
/// Return the natural logarithm of x
122147
/// See https://docs.python.org/3/library/math.html#math.log
123148
abstract log: x: float -> float
149+
/// Return the logarithm of x to the given base
150+
/// See https://docs.python.org/3/library/math.html#math.log
151+
abstract log: x: float * ``base``: float -> float
124152
/// Return the natural logarithm of 1+x (base e)
125153
/// See https://docs.python.org/3/library/math.html#math.log1p
126154
abstract log1p: x: float -> float
@@ -136,6 +164,12 @@ type IExports =
136164
/// Return the square root of x
137165
/// See https://docs.python.org/3/library/math.html#math.sqrt
138166
abstract sqrt: x: float -> float
167+
/// Return 2 raised to the power x (Python 3.11+)
168+
/// See https://docs.python.org/3/library/math.html#math.exp2
169+
abstract exp2: x: float -> float
170+
/// Return the cube root of x (Python 3.11+)
171+
/// See https://docs.python.org/3/library/math.html#math.cbrt
172+
abstract cbrt: x: float -> float
139173

140174
// ========================================================================
141175
// Trigonometric functions

‎test/TestMath.fs‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,60 @@ let ``test gamma works`` () =
224224
[<Fact>]
225225
let ``test lgamma works`` () =
226226
math.lgamma 1.0 |> equal 0.0
227+
228+
[<Fact>]
229+
let ``test log with base works`` () =
230+
math.log (8.0, 2.0) |> equal 3.0
231+
math.log (100.0, 10.0) |> equal 2.0
232+
233+
[<Fact>]
234+
let ``test ldexp works`` () =
235+
math.ldexp (1.0, 3n) |> equal 8.0
236+
math.ldexp (0.5, 2n) |> equal 2.0
237+
238+
[<Fact>]
239+
let ``test frexp works`` () =
240+
let m, e = math.frexp 8.0
241+
m |> equal 0.5
242+
e |> equal 4
243+
244+
[<Fact>]
245+
let ``test modf works`` () =
246+
let frac, intPart = math.modf 3.7
247+
(frac > 0.699 && frac < 0.701) |> equal true
248+
intPart |> equal 3.0
249+
250+
[<Fact>]
251+
let ``test remainder works`` () =
252+
math.remainder (10.0, 3.0) |> equal 1.0
253+
math.remainder (5.0, 2.0) |> equal 1.0
254+
255+
[<Fact>]
256+
let ``test isclose works`` () =
257+
math.isclose (1.0, 1.0) |> equal true
258+
math.isclose (1.0, 2.0) |> equal false
259+
260+
[<Fact>]
261+
let ``test isclose with tolerances works`` () =
262+
math.isclose (1.0, 1.001, rel_tol=0.01) |> equal true
263+
math.isclose (1.0, 2.0, abs_tol=0.1) |> equal false
264+
265+
[<Fact>]
266+
let ``test nextafter works`` () =
267+
let x = math.nextafter (1.0, 2.0)
268+
(x > 1.0) |> equal true
269+
270+
[<Fact>]
271+
let ``test ulp works`` () =
272+
let x = math.ulp 1.0
273+
(x > 0.0) |> equal true
274+
275+
[<Fact>]
276+
let ``test exp2 works`` () =
277+
math.exp2 3.0 |> equal 8.0
278+
math.exp2 0.0 |> equal 1.0
279+
280+
[<Fact>]
281+
let ``test cbrt works`` () =
282+
math.isclose (math.cbrt 27.0, 3.0) |> equal true
283+
math.isclose (math.cbrt 8.0, 2.0) |> equal true

0 commit comments

Comments
 (0)