Skip to content

Commit aa8f163

Browse files
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>
1 parent 8f33975 commit aa8f163

3 files changed

Lines changed: 92 additions & 10 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,9 @@ All notable changes to this project will be documented in this file.
99

1010
## Unreleased
1111

12-
### 🐞 Bug Fixes
13-
14-
* Fix `math.factorial` binding: changed signature from `float -> float` to `int -> int` to match Python 3.12+ where float arguments raise `TypeError`. Fixes test to use integer literals.
15-
* Fix `math.copysign` binding: `y` parameter was incorrectly typed as `int`, now correctly `float`
16-
* Fix `math.fmod` binding: parameters were incorrectly typed as `int -> int -> int`, now correctly `float * float -> float`
17-
* Fix `math.comb`, `math.pow`, `math.atan2`, `math.dist` bindings: converted curried parameter syntax to tupled for consistency
18-
1912
### ✨ Enhancements
2013

21-
* Add missing `math` module constants: `pi`, `e`, `tau`, `inf`, `nan`
22-
* Add missing `math` module functions: `sqrt`, `degrees`, `radians`, `trunc`, `hypot`, `fsum`, `isqrt`, `prod`, `perm`, `acosh`, `asinh`, `atanh`, `cosh`, `sinh`, `tanh`, `erf`, `erfc`, `gamma`, `lgamma`
23-
* Fix `math.dist` signature to accept float arrays (for multi-dimensional distance)
14+
* Add missing `math` module functions: `log` with base, `ldexp`, `frexp`, `modf`, `remainder`, `isclose`, `nextafter`, `ulp`, `exp2`, `cbrt`
2415

2516
## 5.0.0-rc.3 - 2026-04-16
2617

‎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: int -> 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, 3) |> equal 8.0
236+
math.ldexp (0.5, 2) |> 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.cbrt 27.0 |> equal 3.0
283+
math.cbrt 8.0 |> equal 2.0

0 commit comments

Comments
 (0)