Reimplement the generic fmod
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
#![allow(clippy::excessive_precision)]
|
#![allow(clippy::excessive_precision)]
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::int_plus_one)]
|
#![allow(clippy::int_plus_one)]
|
||||||
|
#![allow(clippy::just_underscores_and_digits)]
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![allow(clippy::many_single_char_names)]
|
||||||
#![allow(clippy::mixed_case_hex_literals)]
|
#![allow(clippy::mixed_case_hex_literals)]
|
||||||
#![allow(clippy::needless_late_init)]
|
#![allow(clippy::needless_late_init)]
|
||||||
|
|||||||
@@ -1,84 +1,68 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT OR Apache-2.0 */
|
||||||
/* origin: musl src/math/fmod.c. Ported to generic Rust algorithm in 2025, TG. */
|
|
||||||
|
|
||||||
use super::super::{CastFrom, Float, Int, MinInt};
|
use super::super::{CastFrom, Float, Int, MinInt};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fmod<F: Float>(x: F, y: F) -> F {
|
pub fn fmod<F: Float>(x: F, y: F) -> F {
|
||||||
let zero = F::Int::ZERO;
|
let _1 = F::Int::ONE;
|
||||||
let one = F::Int::ONE;
|
let sx = x.to_bits() & F::SIGN_MASK;
|
||||||
let mut ix = x.to_bits();
|
let ux = x.to_bits() & !F::SIGN_MASK;
|
||||||
let mut iy = y.to_bits();
|
let uy = y.to_bits() & !F::SIGN_MASK;
|
||||||
let mut ex = x.ex().signed();
|
|
||||||
let mut ey = y.ex().signed();
|
|
||||||
let sx = ix & F::SIGN_MASK;
|
|
||||||
|
|
||||||
if iy << 1 == zero || y.is_nan() || ex == F::EXP_SAT as i32 {
|
// Cases that return NaN:
|
||||||
|
// NaN % _
|
||||||
|
// Inf % _
|
||||||
|
// _ % NaN
|
||||||
|
// _ % 0
|
||||||
|
let x_nan_or_inf = ux & F::EXP_MASK == F::EXP_MASK;
|
||||||
|
let y_nan_or_zero = uy.wrapping_sub(_1) & F::EXP_MASK == F::EXP_MASK;
|
||||||
|
if x_nan_or_inf | y_nan_or_zero {
|
||||||
return (x * y) / (x * y);
|
return (x * y) / (x * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ix << 1 <= iy << 1 {
|
if ux < uy {
|
||||||
if ix << 1 == iy << 1 {
|
// |x| < |y|
|
||||||
return F::ZERO * x;
|
|
||||||
}
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* normalize x and y */
|
let (num, ex) = into_sig_exp::<F>(ux);
|
||||||
if ex == 0 {
|
let (div, ey) = into_sig_exp::<F>(uy);
|
||||||
let i = ix << (F::EXP_BITS + 1);
|
|
||||||
ex -= i.leading_zeros() as i32;
|
|
||||||
ix <<= -ex + 1;
|
|
||||||
} else {
|
|
||||||
ix &= F::Int::MAX >> F::EXP_BITS;
|
|
||||||
ix |= one << F::SIG_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ey == 0 {
|
// To compute `(num << ex) % (div << ey)`, first
|
||||||
let i = iy << (F::EXP_BITS + 1);
|
// evaluate `rem = (num << (ex - ey)) % div` ...
|
||||||
ey -= i.leading_zeros() as i32;
|
let rem = reduction(num, ex - ey, div);
|
||||||
iy <<= -ey + 1;
|
// ... so the result will be `rem << ey`
|
||||||
} else {
|
|
||||||
iy &= F::Int::MAX >> F::EXP_BITS;
|
|
||||||
iy |= one << F::SIG_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* x mod y */
|
if rem.is_zero() {
|
||||||
while ex > ey {
|
// Return zero with the sign of `x`
|
||||||
let i = ix.wrapping_sub(iy);
|
return F::from_bits(sx);
|
||||||
if i >> (F::BITS - 1) == zero {
|
};
|
||||||
if i == zero {
|
|
||||||
return F::ZERO * x;
|
|
||||||
}
|
|
||||||
ix = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
ix <<= 1;
|
// We would shift `rem` up by `ey`, but have to stop at `F::SIG_BITS`
|
||||||
ex -= 1;
|
let shift = ey.min(F::SIG_BITS - rem.ilog2());
|
||||||
}
|
// Anything past that is added to the exponent field
|
||||||
|
let bits = (rem << shift) + (F::Int::cast_from(ey - shift) << F::SIG_BITS);
|
||||||
let i = ix.wrapping_sub(iy);
|
F::from_bits(sx + bits)
|
||||||
if i >> (F::BITS - 1) == zero {
|
}
|
||||||
if i == zero {
|
|
||||||
return F::ZERO * x;
|
/// Given the bits of a finite float, return a tuple of
|
||||||
}
|
/// - the mantissa with the implicit bit (0 if subnormal, 1 otherwise)
|
||||||
|
/// - the additional exponent past 1, (0 for subnormal, 0 or more otherwise)
|
||||||
ix = i;
|
fn into_sig_exp<F: Float>(mut bits: F::Int) -> (F::Int, u32) {
|
||||||
}
|
bits &= !F::SIGN_MASK;
|
||||||
|
// Subtract 1 from the exponent, clamping at 0
|
||||||
let shift = ix.leading_zeros().saturating_sub(F::EXP_BITS);
|
let sat = bits.checked_sub(F::IMPLICIT_BIT).unwrap_or(F::Int::ZERO);
|
||||||
ix <<= shift;
|
(
|
||||||
ex -= shift as i32;
|
bits - (sat & F::EXP_MASK),
|
||||||
|
u32::cast_from(sat >> F::SIG_BITS),
|
||||||
/* scale result */
|
)
|
||||||
if ex > 0 {
|
}
|
||||||
ix -= one << F::SIG_BITS;
|
|
||||||
ix |= F::Int::cast_from(ex) << F::SIG_BITS;
|
/// Compute the remainder `(x * 2.pow(e)) % y` without overflow.
|
||||||
} else {
|
fn reduction<I: Int>(mut x: I, e: u32, y: I) -> I {
|
||||||
ix >>= -ex + 1;
|
x %= y;
|
||||||
}
|
for _ in 0..e {
|
||||||
|
x <<= 1;
|
||||||
ix |= sx;
|
x = x.checked_sub(y).unwrap_or(x);
|
||||||
|
}
|
||||||
F::from_bits(ix)
|
x
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ pub trait Int:
|
|||||||
+ PartialOrd
|
+ PartialOrd
|
||||||
+ ops::AddAssign
|
+ ops::AddAssign
|
||||||
+ ops::SubAssign
|
+ ops::SubAssign
|
||||||
|
+ ops::MulAssign
|
||||||
|
+ ops::DivAssign
|
||||||
|
+ ops::RemAssign
|
||||||
+ ops::BitAndAssign
|
+ ops::BitAndAssign
|
||||||
+ ops::BitOrAssign
|
+ ops::BitOrAssign
|
||||||
+ ops::BitXorAssign
|
+ ops::BitXorAssign
|
||||||
@@ -51,6 +54,7 @@ pub trait Int:
|
|||||||
+ ops::Sub<Output = Self>
|
+ ops::Sub<Output = Self>
|
||||||
+ ops::Mul<Output = Self>
|
+ ops::Mul<Output = Self>
|
||||||
+ ops::Div<Output = Self>
|
+ ops::Div<Output = Self>
|
||||||
|
+ ops::Rem<Output = Self>
|
||||||
+ ops::Shl<i32, Output = Self>
|
+ ops::Shl<i32, Output = Self>
|
||||||
+ ops::Shl<u32, Output = Self>
|
+ ops::Shl<u32, Output = Self>
|
||||||
+ ops::Shr<i32, Output = Self>
|
+ ops::Shr<i32, Output = Self>
|
||||||
|
|||||||
Reference in New Issue
Block a user