Implement euclidean division and remainder (#2678)

This commit is contained in:
frozolotl 2023-11-17 19:55:57 +01:00 committed by GitHub
parent f5b3af3c1b
commit 43f90b2159
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 1 deletions

View File

@ -50,6 +50,8 @@ fn module() -> Module {
scope.define_func::<even>();
scope.define_func::<odd>();
scope.define_func::<rem>();
scope.define_func::<div_euclid>();
scope.define_func::<rem_euclid>();
scope.define_func::<quo>();
scope.define("inf", f64::INFINITY);
scope.define("nan", f64::NAN);
@ -790,7 +792,10 @@ pub fn odd(
/// in magnitude than `y`.
///
/// ```example
/// #calc.rem(20, 6) \
/// #calc.rem(7, 3) \
/// #calc.rem(7, -3) \
/// #calc.rem(-7, 3) \
/// #calc.rem(-7, -3) \
/// #calc.rem(1.75, 0.5)
/// ```
#[func(title = "Remainder")]
@ -806,6 +811,57 @@ pub fn rem(
Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem))
}
/// Performs euclidean division of two numbers.
///
/// The result of this computation is that of a division rounded to the integer
/// `{n}` such that the dividend is greater than or equal to `{n}` times the divisor.
///
/// ```example
/// #calc.div-euclid(7, 3) \
/// #calc.div-euclid(7, -3) \
/// #calc.div-euclid(-7, 3) \
/// #calc.div-euclid(-7, -3) \
/// #calc.div-euclid(1.75, 0.5)
/// ```
#[func(title = "Euclidean Division")]
pub fn div_euclid(
/// The dividend of the division.
dividend: Num,
/// The divisor of the division.
divisor: Spanned<Num>,
) -> SourceResult<Num> {
if divisor.v.float() == 0.0 {
bail!(divisor.span, "divisor must not be zero");
}
Ok(dividend.apply2(divisor.v, i64::div_euclid, f64::div_euclid))
}
/// This calculates the least nonnegative remainder of a division.
///
/// Warning: Due to a floating point round-off error, the remainder may equal the absolute
/// value of the divisor if the dividend is much smaller in magnitude than the divisor
/// and the dividend is negative. This only applies for floating point inputs.
///
/// ```example
/// #calc.rem-euclid(7, 3) \
/// #calc.rem-euclid(7, -3) \
/// #calc.rem-euclid(-7, 3) \
/// #calc.rem-euclid(-7, -3) \
/// #calc.rem(1.75, 0.5)
/// ```
#[func(title = "Euclidean Remainder")]
pub fn rem_euclid(
/// The dividend of the remainder.
dividend: Num,
/// The divisor of the remainder.
divisor: Spanned<Num>,
) -> SourceResult<Num> {
if divisor.v.float() == 0.0 {
bail!(divisor.span, "divisor must not be zero");
}
Ok(dividend.apply2(divisor.v, i64::rem_euclid, f64::rem_euclid))
}
/// Calculates the quotient (floored division) of two numbers.
///
/// ```example

View File

@ -76,6 +76,38 @@
// Error: 16-19 divisor must not be zero
#calc.rem(3.0, 0.0)
---
// Test the `div-euclid` function.
#test(calc.div-euclid(7, 3), 2)
#test(calc.div-euclid(7, -3), -2)
#test(calc.div-euclid(-7, 3), -3)
#test(calc.div-euclid(-7, -3), 3)
#test(calc.div-euclid(2.5, 2), 1)
---
// Error: 21-22 divisor must not be zero
#calc.div-euclid(5, 0)
---
// Error: 23-26 divisor must not be zero
#calc.div-euclid(3.0, 0.0)
---
// Test the `rem-euclid` function.
#test(calc.rem-euclid(7, 3), 1)
#test(calc.rem-euclid(7, -3), 1)
#test(calc.rem-euclid(-7, 3), 2)
#test(calc.rem-euclid(-7, -3), 2)
#test(calc.rem-euclid(2.5, 2), 0.5)
---
// Error: 21-22 divisor must not be zero
#calc.rem-euclid(5, 0)
---
// Error: 23-26 divisor must not be zero
#calc.rem-euclid(3.0, 0.0)
---
// Test the `quo` function.
#test(calc.quo(1, 1), 1)