fix: Handle i64::MIN / -1 special case

Rust panics on i64::MIN % -1i64, so Number::divide needs to handle it specially.
This commit is contained in:
Jay Lorch
2026-07-27 10:22:28 -07:00
committed by GitHub
parent 6608a9f05e
commit e468255657

View File

@@ -591,7 +591,11 @@ impl Number {
} }
} }
(Number::Int(a), Number::Int(b)) => { (Number::Int(a), Number::Int(b)) => {
if *a % *b == 0 { if *a == i64::MIN && *b == -1 {
// Rust panics on i64::MIN % -1i64, so handle it specially
let quotient = BigInt::from(*a) / BigInt::from(*b);
Ok(Number::from_bigint_owned(quotient))
} else if *a % *b == 0 {
if let Some(q) = a.checked_div(*b) { if let Some(q) = a.checked_div(*b) {
Ok(Number::Int(q)) Ok(Number::Int(q))
} else { } else {
@@ -991,3 +995,41 @@ fn scientific_parts_to_bigint(mantissa: &str, exponent: i32) -> Option<BigInt> {
Some(value) Some(value)
} }
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)] // tests expect() to assert arithmetic results
use super::*;
/// Regression test: `i64::MIN / -1` overflows `i64` and panics in Rust's
/// native integer division/remainder. `divide` must promote the result
/// instead of panicking.
#[test]
fn i64_min_by_negative_one() {
let quotient = Number::Int(i64::MIN)
.divide(&Number::Int(-1))
.expect("division should succeed");
// 2^63 does not fit in i64, but does fit in u64.
assert_eq!(quotient.as_u64(), Some(9_223_372_036_854_775_808));
assert_eq!(quotient.as_i64(), None);
assert_eq!(quotient.as_i128(), Some(9_223_372_036_854_775_808));
assert_eq!(
*quotient.to_big().expect("to_big should succeed"),
-BigInt::from(i64::MIN)
);
// The same overflow case reached via the mixed `Int`/`BigInt` path.
let big_quotient = Number::Int(i64::MIN)
.divide(&Number::BigInt(Rc::new(BigInt::from(-1))))
.expect("division should succeed");
assert_eq!(big_quotient.as_u64(), Some(9_223_372_036_854_775_808));
// `i64::MIN % -1` also panics natively; the result must be zero.
let remainder = Number::Int(i64::MIN)
.modulo(&Number::Int(-1))
.expect("modulo should succeed");
assert_eq!(remainder.as_i64(), Some(0));
}
}