Skip to content

Commit 2dc292b

Browse files
quaternictgross35
authored andcommitted
Reimplement the generic fmod
1 parent 4f4dbe8 commit 2dc292b

File tree

3 files changed

+55
-66
lines changed

3 files changed

+55
-66
lines changed

libm/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![allow(clippy::excessive_precision)]
1515
#![allow(clippy::float_cmp)]
1616
#![allow(clippy::int_plus_one)]
17+
#![allow(clippy::just_underscores_and_digits)]
1718
#![allow(clippy::many_single_char_names)]
1819
#![allow(clippy::mixed_case_hex_literals)]
1920
#![allow(clippy::needless_late_init)]

libm/src/math/generic/fmod.rs

+50-66
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,68 @@
1-
/* SPDX-License-Identifier: MIT */
2-
/* origin: musl src/math/fmod.c. Ported to generic Rust algorithm in 2025, TG. */
3-
1+
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
42
use super::super::{CastFrom, Float, Int, MinInt};
53

64
#[inline]
75
pub fn fmod<F: Float>(x: F, y: F) -> F {
8-
let zero = F::Int::ZERO;
9-
let one = F::Int::ONE;
10-
let mut ix = x.to_bits();
11-
let mut iy = y.to_bits();
12-
let mut ex = x.ex().signed();
13-
let mut ey = y.ex().signed();
14-
let sx = ix & F::SIGN_MASK;
6+
let _1 = F::Int::ONE;
7+
let sx = x.to_bits() & F::SIGN_MASK;
8+
let ux = x.to_bits() & !F::SIGN_MASK;
9+
let uy = y.to_bits() & !F::SIGN_MASK;
1510

16-
if iy << 1 == zero || y.is_nan() || ex == F::EXP_SAT as i32 {
11+
// Cases that return NaN:
12+
// NaN % _
13+
// Inf % _
14+
// _ % NaN
15+
// _ % 0
16+
let x_nan_or_inf = ux & F::EXP_MASK == F::EXP_MASK;
17+
let y_nan_or_zero = uy.wrapping_sub(_1) & F::EXP_MASK == F::EXP_MASK;
18+
if x_nan_or_inf | y_nan_or_zero {
1719
return (x * y) / (x * y);
1820
}
1921

20-
if ix << 1 <= iy << 1 {
21-
if ix << 1 == iy << 1 {
22-
return F::ZERO * x;
23-
}
22+
if ux < uy {
23+
// |x| < |y|
2424
return x;
2525
}
2626

27-
/* normalize x and y */
28-
if ex == 0 {
29-
let i = ix << (F::EXP_BITS + 1);
30-
ex -= i.leading_zeros() as i32;
31-
ix <<= -ex + 1;
32-
} else {
33-
ix &= F::Int::MAX >> F::EXP_BITS;
34-
ix |= one << F::SIG_BITS;
35-
}
36-
37-
if ey == 0 {
38-
let i = iy << (F::EXP_BITS + 1);
39-
ey -= i.leading_zeros() as i32;
40-
iy <<= -ey + 1;
41-
} else {
42-
iy &= F::Int::MAX >> F::EXP_BITS;
43-
iy |= one << F::SIG_BITS;
44-
}
45-
46-
/* x mod y */
47-
while ex > ey {
48-
let i = ix.wrapping_sub(iy);
49-
if i >> (F::BITS - 1) == zero {
50-
if i == zero {
51-
return F::ZERO * x;
52-
}
53-
ix = i;
54-
}
27+
let (num, ex) = into_sig_exp::<F>(ux);
28+
let (div, ey) = into_sig_exp::<F>(uy);
5529

56-
ix <<= 1;
57-
ex -= 1;
58-
}
30+
// To compute `(num << ex) % (div << ey)`, first
31+
// evaluate `rem = (num << (ex - ey)) % div` ...
32+
let rem = reduction(num, ex - ey, div);
33+
// ... so the result will be `rem << ey`
5934

60-
let i = ix.wrapping_sub(iy);
61-
if i >> (F::BITS - 1) == zero {
62-
if i == zero {
63-
return F::ZERO * x;
64-
}
35+
if rem.is_zero() {
36+
// Return zero with the sign of `x`
37+
return F::from_bits(sx);
38+
};
6539

66-
ix = i;
67-
}
40+
// We would shift `rem` up by `ey`, but have to stop at `F::SIG_BITS`
41+
let shift = ey.min(F::SIG_BITS - rem.ilog2());
42+
// Anything past that is added to the exponent field
43+
let bits = (rem << shift) + (F::Int::cast_from(ey - shift) << F::SIG_BITS);
44+
F::from_bits(sx + bits)
45+
}
6846

69-
let shift = ix.leading_zeros().saturating_sub(F::EXP_BITS);
70-
ix <<= shift;
71-
ex -= shift as i32;
47+
/// Given the bits of a finite float, return a tuple of
48+
/// - the mantissa with the implicit bit (0 if subnormal, 1 otherwise)
49+
/// - the additional exponent past 1, (0 for subnormal, 0 or more otherwise)
50+
fn into_sig_exp<F: Float>(mut bits: F::Int) -> (F::Int, u32) {
51+
bits &= !F::SIGN_MASK;
52+
// Subtract 1 from the exponent, clamping at 0
53+
let sat = bits.checked_sub(F::IMPLICIT_BIT).unwrap_or(F::Int::ZERO);
54+
(
55+
bits - (sat & F::EXP_MASK),
56+
u32::cast_from(sat >> F::SIG_BITS),
57+
)
58+
}
7259

73-
/* scale result */
74-
if ex > 0 {
75-
ix -= one << F::SIG_BITS;
76-
ix |= F::Int::cast_from(ex) << F::SIG_BITS;
77-
} else {
78-
ix >>= -ex + 1;
60+
/// Compute the remainder `(x * 2.pow(e)) % y` without overflow.
61+
fn reduction<I: Int>(mut x: I, e: u32, y: I) -> I {
62+
x %= y;
63+
for _ in 0..e {
64+
x <<= 1;
65+
x = x.checked_sub(y).unwrap_or(x);
7966
}
80-
81-
ix |= sx;
82-
83-
F::from_bits(ix)
67+
x
8468
}

libm/src/math/support/int_traits.rs

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub trait Int:
4040
+ PartialOrd
4141
+ ops::AddAssign
4242
+ ops::SubAssign
43+
+ ops::MulAssign
44+
+ ops::DivAssign
45+
+ ops::RemAssign
4346
+ ops::BitAndAssign
4447
+ ops::BitOrAssign
4548
+ ops::BitXorAssign
@@ -51,6 +54,7 @@ pub trait Int:
5154
+ ops::Sub<Output = Self>
5255
+ ops::Mul<Output = Self>
5356
+ ops::Div<Output = Self>
57+
+ ops::Rem<Output = Self>
5458
+ ops::Shl<i32, Output = Self>
5559
+ ops::Shl<u32, Output = Self>
5660
+ ops::Shr<i32, Output = Self>

0 commit comments

Comments
 (0)