Fix MOD returning wrong sign and magnitude for tiny divisors

Found while testing crates/functions/src/math.rs:305-319

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-23 21:43:22 +02:00
co-authored by Claude Haiku 5.5
parent 15a705e234
commit c746eea876
+15 -2
View File
@@ -306,8 +306,8 @@ fn modulo(x: f64, d: f64) -> R<f64> {
if d == 0.0 {
return Err(CellError::Div0);
}
let q = (x / d).floor();
let r = x - d * q;
// `%` is an exact remainder (truncated), so |r| < |d| even for tiny divisors.
let r = x % d;
// Keep the sign of the divisor and avoid returning |d| from rounding.
if r != 0.0 && (r < 0.0) != (d < 0.0) {
return Ok(r + d);
@@ -1442,6 +1442,19 @@ mod tests {
close(ev1("ODD", 0.0), 1.0);
}
#[test]
fn mod_tiny_divisor_stays_in_range() {
for (x, d) in [(3.0, 1e-300), (-1.0, 1e-300), (3.0, -1e-300), (-1.0, -1e-300)] {
match ev2("MOD", x, d) {
Value::Number(r) => {
assert!(r == 0.0 || (r < 0.0) == (d < 0.0), "MOD({x}, {d}) = {r}");
assert!(r.abs() < d.abs(), "MOD({x}, {d}) = {r}");
}
other => panic!("MOD({x}, {d}) = {other:?}"),
}
}
}
#[test]
fn arithmetic() {
close(ev2("MOD", 3.0, 2.0), 1.0);