pub(crate) const fn div_nearest_ties_away(numerator: i64, denominator: i64) -> i64 {
debug_assert!(denominator != 0);
let numerator_abs = numerator.unsigned_abs();
let denominator_abs = denominator.unsigned_abs();
let quotient = (numerator_abs + denominator_abs / 2) / denominator_abs;
debug_assert!(quotient <= i64::MAX as u64);
if (numerator < 0) == (denominator < 0) {
quotient as i64
} else {
(quotient as i64).wrapping_neg()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ties_round_away_from_zero_in_every_sign_combination() {
assert_eq!(div_nearest_ties_away(1, 2), 1);
assert_eq!(div_nearest_ties_away(-1, 2), -1);
assert_eq!(div_nearest_ties_away(1, -2), -1);
assert_eq!(div_nearest_ties_away(-1, -2), 1);
assert_eq!(div_nearest_ties_away(3, 2), 2);
assert_eq!(div_nearest_ties_away(-3, 2), -2);
assert_eq!(div_nearest_ties_away(3, -2), -2);
assert_eq!(div_nearest_ties_away(-3, -2), 2);
}
#[test]
fn non_ties_round_to_nearest() {
assert_eq!(div_nearest_ties_away(1, 3), 0);
assert_eq!(div_nearest_ties_away(2, 3), 1);
assert_eq!(div_nearest_ties_away(-1, 3), 0);
assert_eq!(div_nearest_ties_away(-2, 3), -1);
assert_eq!(div_nearest_ties_away(0, 7), 0);
}
#[test]
fn exact_quotients_are_unchanged() {
assert_eq!(div_nearest_ties_away(10, 5), 2);
assert_eq!(div_nearest_ties_away(-10, 5), -2);
assert_eq!(div_nearest_ties_away(10, -5), -2);
assert_eq!(div_nearest_ties_away(-10, -5), 2);
}
#[test]
fn wide_operands_stay_exact() {
let numerator = i64::from(i32::MIN) * i64::from(u16::MAX);
assert_eq!(
div_nearest_ties_away(numerator, i64::from(u16::MAX)),
i64::from(i32::MIN)
);
let numerator = i64::from(i32::MAX) * i64::from(i32::MAX) + i64::from(i32::MAX);
assert_eq!(div_nearest_ties_away(numerator, 1), numerator);
assert_eq!(div_nearest_ties_away(numerator, -1), -numerator);
}
#[test]
fn matches_the_previous_per_module_implementations() {
fn legacy_stabilize(numerator: i64, denominator: i64) -> i64 {
if numerator >= 0 {
(numerator + denominator / 2) / denominator
} else {
-((-numerator + denominator / 2) / denominator)
}
}
for numerator in -1_000i64..=1_000 {
for denominator in [1i64, 2, 3, 7, 255, 65_535] {
assert_eq!(
div_nearest_ties_away(numerator, denominator),
legacy_stabilize(numerator, denominator),
"{numerator} / {denominator}"
);
}
}
}
}