math.big: change the handling of negatives in div_mod() to match gmp and Julia, add tests (#24713)

This commit is contained in:
Mike 2025-06-15 10:51:40 +03:00 committed by GitHub
parent 12c20e3c1f
commit 6e271b2ae6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 25 deletions

View File

@ -248,6 +248,20 @@ struct DivModTest {
// vfmt off // vfmt off
const div_mod_test_data = [ const div_mod_test_data = [
DivModTest{0, 7, 0, 0},
DivModTest{0, -7, 0, 0},
DivModTest{3, 7, 0, 3},
DivModTest{-3, -7, 0, -3},
DivModTest{-3, 7, 0, -3},
DivModTest{3, -7, 0, 3},
DivModTest{7, 3, 2, 1},
DivModTest{-7, -3, 2, -1},
DivModTest{-7, 3, -2, -1},
DivModTest{7, -3, -2, 1},
DivModTest{8, 2, 4, 0},
DivModTest{-8, -2, 4, 0},
DivModTest{-8, 2, -4, 0},
DivModTest{8, -2, -4, 0},
DivModTest{13, 10, 1, 3}, DivModTest{13, 10, 1, 3},
DivModTest{13, 9, 1, 4}, DivModTest{13, 9, 1, 4},
DivModTest{7, 5, 1, 2}, DivModTest{7, 5, 1, 2},

View File

@ -415,38 +415,31 @@ pub fn (multiplicand Integer) * (multiplier Integer) Integer {
// //
// DO NOT use this method if the divisor has any chance of being 0. // DO NOT use this method if the divisor has any chance of being 0.
fn (dividend Integer) div_mod_internal(divisor Integer) (Integer, Integer) { fn (dividend Integer) div_mod_internal(divisor Integer) (Integer, Integer) {
$if debug {
assert divisor.signum != 0
}
if dividend.signum == 0 {
return zero_int, zero_int
}
if divisor == one_int {
return dividend.clone(), zero_int
}
if divisor.signum == -1 {
q, r := dividend.div_mod_internal(divisor.neg())
return q.neg(), r
}
if dividend.signum == -1 {
q, r := dividend.neg().div_mod_internal(divisor)
if r.signum == 0 {
return q.neg(), zero_int
} else {
return q.neg() - one_int, divisor - r
}
}
// Division for positive integers
mut q := []u32{cap: int_max(1, dividend.digits.len - divisor.digits.len + 1)} mut q := []u32{cap: int_max(1, dividend.digits.len - divisor.digits.len + 1)}
mut r := []u32{cap: dividend.digits.len} mut r := []u32{cap: dividend.digits.len}
mut q_signum := 0
mut r_signum := 0
divide_digit_array(dividend.digits, divisor.digits, mut q, mut r) divide_digit_array(dividend.digits, divisor.digits, mut q, mut r)
if dividend.signum > 0 && divisor.signum > 0 {
q_signum = 1
r_signum = 1
} else if dividend.signum > 0 && divisor.signum < 0 {
q_signum = -1
r_signum = 1
} else if dividend.signum < 0 && divisor.signum > 0 {
q_signum = -1
r_signum = -1
} else {
q_signum = 1
r_signum = -1
}
quotient := Integer{ quotient := Integer{
signum: if q.len == 0 { 0 } else { 1 } signum: if q.len == 0 { 0 } else { q_signum }
digits: q digits: q
} }
remainder := Integer{ remainder := Integer{
signum: if r.len == 0 { 0 } else { 1 } signum: if r.len == 0 { 0 } else { r_signum }
digits: r digits: r
} }
return quotient, remainder return quotient, remainder