From 25561972081e84a5773b53af13337dcd691f81e8 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 15 Apr 2025 04:20:17 +0000 Subject: [PATCH 1/2] fmod: Add regression tests for subnormal issue From discussion at [1] our loop count calculation is incorrect, causing an issue with subnormal numbers. Add test cases for known failures. [1]: https://github.com/rust-lang/libm/pull/469#discussion_r2012473920 --- crates/libm-test/src/gen/case_list.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/libm-test/src/gen/case_list.rs b/crates/libm-test/src/gen/case_list.rs index 7cb9897d8..e3628d51c 100644 --- a/crates/libm-test/src/gen/case_list.rs +++ b/crates/libm-test/src/gen/case_list.rs @@ -403,11 +403,33 @@ fn fminimum_numf128_cases() -> Vec> { } fn fmod_cases() -> Vec> { - vec![] + let mut v = vec![]; + TestCase::append_pairs( + &mut v, + &[ + // Previous failure with incorrect loop iteration + // + ((2.1, 3.123e-320), Some(2.0696e-320)), + ((2.1, 2.253547e-318), Some(1.772535e-318)), + ], + ); + v } fn fmodf_cases() -> Vec> { - vec![] + let mut v = vec![]; + TestCase::append_pairs( + &mut v, + &[ + // Previous failure with incorrect loop iteration + // + ((2.1, 8.858e-42), Some(8.085e-42)), + ((2.1, 6.39164e-40), Some(6.1636e-40)), + ((5.5, 6.39164e-40), Some(4.77036e-40)), + ((-151.189, 6.39164e-40), Some(-5.64734e-40)), + ], + ); + v } #[cfg(f128_enabled)] From 92933cde1d2bee44ea5a3e704e1d5c9f78f1b250 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 16 Apr 2025 18:43:25 +0000 Subject: [PATCH 2/2] fmod: Correct the normalization of subnormals Discussed at [1], there was an off-by-one mistake when converting from the loop routine to using `leading_zeros` for normalization. Currently, using `EXP_BITS` has the effect that `ix` after the branch has its MSB _one bit to the left_ of the implicit bit's position, whereas a shift by `EXP_BITS + 1` ensures that the MSB is exactly at the implicit bit's position, matching what is done for normals (where the implicit bit is set to be explicit). This doesn't seem to have any effect in our implementation since the failing test cases from [1] appear to still have correct results. Since the result of using `EXP_BITS + 1` is more consistent with what is done for normals, apply this here. [1]: https://github.com/rust-lang/libm/pull/469#discussion_r2012473920 --- src/math/generic/fmod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/math/generic/fmod.rs b/src/math/generic/fmod.rs index c74b593d5..cd23350ea 100644 --- a/src/math/generic/fmod.rs +++ b/src/math/generic/fmod.rs @@ -26,7 +26,7 @@ pub fn fmod(x: F, y: F) -> F { /* normalize x and y */ if ex == 0 { - let i = ix << F::EXP_BITS; + let i = ix << (F::EXP_BITS + 1); ex -= i.leading_zeros() as i32; ix <<= -ex + 1; } else { @@ -35,7 +35,7 @@ pub fn fmod(x: F, y: F) -> F { } if ey == 0 { - let i = iy << F::EXP_BITS; + let i = iy << (F::EXP_BITS + 1); ey -= i.leading_zeros() as i32; iy <<= -ey + 1; } else {