1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

math.big: add checked division methods (#18924)

This commit is contained in:
phoebe 2023-07-21 19:06:54 +02:00 committed by GitHub
parent 4a543c5f51
commit 7b306e9b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 11 deletions

View File

@ -573,6 +573,11 @@ fn test_div_mod() {
assert q == eq assert q == eq
assert r == er assert r == er
} }
// an extra test for checked division by zero
if _, _ := div_mod_test_data[0].dividend.parse().div_mod_checked(TestInteger(0).parse()) {
assert false, 'Division by 0 should return an error'
}
} }
fn test_comparison() { fn test_comparison() {

View File

@ -369,12 +369,16 @@ pub fn (multiplicand Integer) * (multiplier Integer) Integer {
} }
} }
// div_mod returns the quotient and remainder from the division of the integers `dividend` divided by `divisor`. // div_mod_internal is an entirely unchecked (in terms of division by zero) method for division.
pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) { // This should only be used for internal calculations involving a definitive non-zero
// Quick exits // divisor.
if divisor.signum == 0 { //
panic('Cannot divide by zero') // DO NOT use this method if the divisor has any chance of being 0.
fn (dividend Integer) div_mod_internal(divisor Integer) (Integer, Integer) {
$if debug {
assert divisor.signum != 0
} }
if dividend.signum == 0 { if dividend.signum == 0 {
return zero_int, zero_int return zero_int, zero_int
} }
@ -382,11 +386,11 @@ pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
return dividend.clone(), zero_int return dividend.clone(), zero_int
} }
if divisor.signum == -1 { if divisor.signum == -1 {
q, r := dividend.div_mod(divisor.neg()) q, r := dividend.div_mod_internal(divisor.neg())
return q.neg(), r return q.neg(), r
} }
if dividend.signum == -1 { if dividend.signum == -1 {
q, r := dividend.neg().div_mod(divisor) q, r := dividend.neg().div_mod_internal(divisor)
if r.signum == 0 { if r.signum == 0 {
return q.neg(), zero_int return q.neg(), zero_int
} else { } else {
@ -408,18 +412,64 @@ pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
return quotient, remainder return quotient, remainder
} }
// div_mod returns the quotient and remainder from the division of the integers `dividend`
// divided by `divisor`.
//
// WARNING: this method will panic if `divisor == 0`. Refer to div_mod_checked for a safer version.
[inline]
pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
if _unlikely_(divisor.signum == 0) {
panic('math.big: Cannot divide by zero')
}
return dividend.div_mod_internal(divisor)
}
// div_mod_checked returns the quotient and remainder from the division of the integers `dividend`
// divided by `divisor`. An error is returned if `divisor == 0`.
[inline]
pub fn (dividend Integer) div_mod_checked(divisor Integer) !(Integer, Integer) {
if _unlikely_(divisor.signum == 0) {
return error('math.big: Cannot divide by zero')
}
return dividend.div_mod_internal(divisor)
}
// / returns the quotient of `dividend` divided by `divisor`. // / returns the quotient of `dividend` divided by `divisor`.
//
// WARNING: this method will panic if `divisor == 0`. For a division method that returns a Result
// refer to `div_checked`.
[inline]
pub fn (dividend Integer) / (divisor Integer) Integer { pub fn (dividend Integer) / (divisor Integer) Integer {
q, _ := dividend.div_mod(divisor) q, _ := dividend.div_mod(divisor)
return q return q
} }
// % returns the remainder of `dividend` divided by `divisor`. // % returns the remainder of `dividend` divided by `divisor`.
//
// WARNING: this method will panic if `divisor == 0`. For a modular division method that
// returns a Result refer to `mod_checked`.
[inline]
pub fn (dividend Integer) % (divisor Integer) Integer { pub fn (dividend Integer) % (divisor Integer) Integer {
_, r := dividend.div_mod(divisor) _, r := dividend.div_mod(divisor)
return r return r
} }
// div_checked returns the quotient of `dividend` divided by `divisor`
// or an error if `divisor == 0`.
[inline]
pub fn (dividend Integer) div_checked(divisor Integer) !Integer {
q, _ := dividend.div_mod_checked(divisor)!
return q
}
// mod_checked returns the remainder of `dividend` divided by `divisor`
// or an error if `divisor == 0`.
[inline]
pub fn (dividend Integer) mod_checked(divisor Integer) !Integer {
_, r := dividend.div_mod_checked(divisor)!
return r
}
// mask_bits is the equivalent of `a % 2^n` (only when `a >= 0`), however doing a full division // mask_bits is the equivalent of `a % 2^n` (only when `a >= 0`), however doing a full division
// run for this would be a lot of work when we can simply "cut off" all bits to the left of // run for this would be a lot of work when we can simply "cut off" all bits to the left of
// the `n`th bit. // the `n`th bit.
@ -791,7 +841,7 @@ pub fn (integer Integer) hex() string {
// radix_str returns the string representation of the integer `a` in the specified radix. // radix_str returns the string representation of the integer `a` in the specified radix.
pub fn (integer Integer) radix_str(radix u32) string { pub fn (integer Integer) radix_str(radix u32) string {
if integer.signum == 0 { if integer.signum == 0 || radix == 0 {
return '0' return '0'
} }
return match radix { return match radix {
@ -808,6 +858,9 @@ pub fn (integer Integer) radix_str(radix u32) string {
} }
fn (integer Integer) general_radix_str(radix u32) string { fn (integer Integer) general_radix_str(radix u32) string {
$if debug {
assert radix != 0
}
divisor := integer_from_u32(radix) divisor := integer_from_u32(radix)
mut current := integer.abs() mut current := integer.abs()
@ -815,7 +868,7 @@ fn (integer Integer) general_radix_str(radix u32) string {
mut digit := zero_int mut digit := zero_int
mut rune_array := []rune{cap: current.digits.len * 4} mut rune_array := []rune{cap: current.digits.len * 4}
for current.signum > 0 { for current.signum > 0 {
new_current, digit = current.div_mod(divisor) new_current, digit = current.div_mod_internal(divisor)
rune_array << big.digit_array[digit.int()] rune_array << big.digit_array[digit.int()]
unsafe { digit.free() } unsafe { digit.free() }
unsafe { current.free() } unsafe { current.free() }
@ -1034,7 +1087,8 @@ fn (a Integer) mod_inv(m Integer) Integer {
q, r := if n.bit_len() == b.bit_len() { q, r := if n.bit_len() == b.bit_len() {
one_int, n - b one_int, n - b
} else { } else {
n.div_mod(b) // safe because the loop terminates if b == 0
n.div_mod_internal(b)
} }
n = b n = b

View File

@ -228,7 +228,7 @@ fn toom3_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u3
p2 := ((ptemp + a2).left_shift(1) - a0) * ((qtemp + b2).left_shift(1) - b0) p2 := ((ptemp + a2).left_shift(1) - a0) * ((qtemp + b2).left_shift(1) - b0)
pinf := a2 * b2 pinf := a2 * b2
mut t2 := (p2 - vm1) / three_int mut t2, _ := (p2 - vm1).div_mod_internal(three_int)
mut tm1 := (p1 - vm1).right_shift(1) mut tm1 := (p1 - vm1).right_shift(1)
mut t1 := p1 - p0 mut t1 := p1 - p0
t2 = (t2 - t1).right_shift(1) t2 = (t2 - t1).right_shift(1)