|
1 |
| -/* SPDX-License-Identifier: MIT */ |
2 |
| -/* origin: musl src/math/fmod.c. Ported to generic Rust algorithm in 2025, TG. */ |
3 |
| - |
4 | 1 | use super::super::{CastFrom, Float, Int, MinInt};
|
5 | 2 |
|
| 3 | +/// Given the bits of a positive float, clamp the exponent field to [0,1] |
| 4 | +fn collapse_exponent<F: Float>(bits: F::Int) -> F::Int { |
| 5 | + let sig = bits & F::SIG_MASK; |
| 6 | + if sig == bits { sig } else { sig | F::IMPLICIT_BIT } |
| 7 | +} |
| 8 | + |
| 9 | +/// Computes (x << e) % y |
| 10 | +fn reduction<I: Int>(mut x: I, e: u32, y: I) -> I { |
| 11 | + x %= y; |
| 12 | + for _ in 0..e { |
| 13 | + x <<= 1; |
| 14 | + x = x.checked_sub(y).unwrap_or(x); |
| 15 | + } |
| 16 | + x |
| 17 | +} |
| 18 | + |
6 | 19 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
7 | 20 | pub fn fmod<F: Float>(x: F, y: F) -> F {
|
8 |
| - let zero = F::Int::ZERO; |
9 |
| - let one = F::Int::ONE; |
| 21 | + let _1 = F::Int::ONE; |
10 | 22 | let mut ix = x.to_bits();
|
11 | 23 | 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; |
15 |
| - |
16 |
| - if iy << 1 == zero || y.is_nan() || ex == F::EXP_SAT as i32 { |
17 |
| - return (x * y) / (x * y); |
18 |
| - } |
19 | 24 |
|
20 |
| - if ix << 1 <= iy << 1 { |
21 |
| - if ix << 1 == iy << 1 { |
22 |
| - return F::ZERO * x; |
23 |
| - } |
24 |
| - return x; |
25 |
| - } |
| 25 | + let sx = ix & F::SIGN_MASK; |
| 26 | + ix &= !F::SIGN_MASK; |
| 27 | + iy &= !F::SIGN_MASK; |
26 | 28 |
|
27 |
| - /* normalize x and y */ |
28 |
| - if ex == 0 { |
29 |
| - let i = ix << F::EXP_BITS; |
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; |
| 29 | + if ix >= F::EXP_MASK { |
| 30 | + // x is nan or inf |
| 31 | + return F::NAN; |
35 | 32 | }
|
36 | 33 |
|
37 |
| - if ey == 0 { |
38 |
| - let i = iy << F::EXP_BITS; |
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; |
| 34 | + if iy.wrapping_sub(_1) >= F::EXP_MASK { |
| 35 | + // y is nan or zero |
| 36 | + return F::NAN; |
44 | 37 | }
|
45 | 38 |
|
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 |
| - } |
55 |
| - |
56 |
| - ix <<= 1; |
57 |
| - ex -= 1; |
58 |
| - } |
| 39 | + if ix < iy { |
| 40 | + // |x| < |y| |
| 41 | + return x; |
| 42 | + }; |
59 | 43 |
|
60 |
| - let i = ix.wrapping_sub(iy); |
61 |
| - if i >> (F::BITS - 1) == zero { |
62 |
| - if i == zero { |
63 |
| - return F::ZERO * x; |
64 |
| - } |
| 44 | + let ex: u32 = x.ex().saturating_sub(1); |
| 45 | + let ey: u32 = y.ex().saturating_sub(1); |
65 | 46 |
|
66 |
| - ix = i; |
67 |
| - } |
| 47 | + let num = collapse_exponent::<F>(ix); |
| 48 | + let div = collapse_exponent::<F>(iy); |
68 | 49 |
|
69 |
| - let shift = ix.leading_zeros().saturating_sub(F::EXP_BITS); |
70 |
| - ix <<= shift; |
71 |
| - ex -= shift as i32; |
| 50 | + let num = reduction(num, ex - ey, div); |
72 | 51 |
|
73 |
| - /* scale result */ |
74 |
| - if ex > 0 { |
75 |
| - ix -= one << F::SIG_BITS; |
76 |
| - ix |= F::Int::cast_from(ex) << F::SIG_BITS; |
| 52 | + if num.is_zero() { |
| 53 | + F::from_bits(sx) |
77 | 54 | } else {
|
78 |
| - ix >>= -ex + 1; |
79 |
| - } |
| 55 | + let ilog = num.ilog2(); |
| 56 | + let shift = (ey + ilog).min(F::SIG_BITS) - ilog; |
| 57 | + let scale = (ey + ilog).saturating_sub(F::SIG_BITS); |
80 | 58 |
|
81 |
| - ix |= sx; |
82 |
| - |
83 |
| - F::from_bits(ix) |
| 59 | + let normalized = num << shift; |
| 60 | + let scaled = normalized + (F::Int::cast_from(scale) << F::SIG_BITS); |
| 61 | + F::from_bits(sx | scaled) |
| 62 | + } |
84 | 63 | }
|
0 commit comments