use decimal_scaled::{D38s12, RoundingMode};
#[test]
fn mul_with_modes() {
let a = D38s12::from_bits(1_500_000_000_000);
let b = D38s12::from_int(2);
for m in [
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Floor,
RoundingMode::Ceiling,
] {
let r = a.mul_with(b, m);
assert_eq!(r.to_bits(), 3_000_000_000_000, "mode {m:?}");
}
}
#[test]
fn div_with_modes() {
let a = D38s12::from_int(1);
let b = D38s12::from_int(3);
let r_even = a.div_with(b, RoundingMode::HalfToEven);
let r_away = a.div_with(b, RoundingMode::HalfAwayFromZero);
let r_trunc = a.div_with(b, RoundingMode::Trunc);
let r_floor = a.div_with(b, RoundingMode::Floor);
let r_ceil = a.div_with(b, RoundingMode::Ceiling);
let bits = [
r_even.to_bits(),
r_away.to_bits(),
r_trunc.to_bits(),
r_floor.to_bits(),
r_ceil.to_bits(),
];
let min = *bits.iter().min().unwrap();
let max = *bits.iter().max().unwrap();
assert!(max - min <= 1, "modes diverged by > 1 LSB: {bits:?}");
}
#[test]
#[should_panic(expected = "attempt to divide by zero")]
fn div_with_zero_panics() {
let _ = D38s12::ONE.div_with(D38s12::ZERO, RoundingMode::HalfToEven);
}
#[test]
fn mul_assign_div_assign() {
let mut v = D38s12::from_bits(1_500_000_000_000); v *= D38s12::from_int(2);
assert_eq!(v.to_bits(), 3_000_000_000_000);
v /= D38s12::from_int(3);
assert_eq!(v.to_bits(), 1_000_000_000_000);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "attempt to multiply with overflow")]
fn mul_with_overflow_panics_in_debug() {
let a = decimal_scaled::D38::<0>::MAX;
let _ = a.mul_with(a, RoundingMode::HalfToEven);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "attempt to divide with overflow")]
fn div_with_overflow_panics_in_debug() {
use decimal_scaled::D38;
let a = D38::<0>::MIN;
let _ = a.div_with(D38::<0>::from_int(-1), RoundingMode::HalfToEven);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "attempt to multiply with overflow")]
fn mul_overflow_panics_in_debug() {
use decimal_scaled::D38;
let a = D38::<0>::MAX;
let _ = a * a;
}
#[cfg(debug_assertions)]
#[test]
#[should_panic(expected = "attempt to divide with overflow")]
fn div_overflow_panics_in_debug() {
use decimal_scaled::D38;
let a = D38::<0>::MIN;
let _ = a / D38::<0>::from_int(-1);
}
#[test]
fn d18_mul_with_modes_exact_at_s18() {
use decimal_scaled::D18;
let a = D18::<18>::from_bits(1_500_000_000_000_000_000);
let b = D18::<18>::from_int(2);
let expected = 3_000_000_000_000_000_000_i64;
for m in [
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Floor,
RoundingMode::Ceiling,
] {
let r = a.mul_with(b, m);
assert_eq!(r.to_bits(), expected, "mode {m:?}");
}
}
#[test]
fn d18_div_with_modes_one_third_at_s18() {
use decimal_scaled::D18;
let a = D18::<18>::from_int(1);
let b = D18::<18>::from_int(3);
let bits = [
a.div_with(b, RoundingMode::HalfToEven).to_bits(),
a.div_with(b, RoundingMode::HalfAwayFromZero).to_bits(),
a.div_with(b, RoundingMode::HalfTowardZero).to_bits(),
a.div_with(b, RoundingMode::Trunc).to_bits(),
a.div_with(b, RoundingMode::Floor).to_bits(),
a.div_with(b, RoundingMode::Ceiling).to_bits(),
];
let min = *bits.iter().min().unwrap();
let max = *bits.iter().max().unwrap();
assert!(max - min <= 1, "modes diverged by > 1 LSB: {bits:?}");
assert_eq!(bits[3], 333_333_333_333_333_333);
assert_eq!(bits[4], 333_333_333_333_333_333);
assert_eq!(bits[5], 333_333_333_333_333_334);
}
#[test]
fn d18_mul_negative_signs_at_s18() {
use decimal_scaled::D18;
let a = D18::<18>::from_bits(1_500_000_000_000_000_000);
let b_pos = D18::<18>::from_int(2);
let b_neg = -b_pos;
let r1 = a.mul_with(b_neg, RoundingMode::HalfToEven);
assert_eq!(r1.to_bits(), -3_000_000_000_000_000_000);
let r2 = (-a).mul_with(b_pos, RoundingMode::HalfToEven);
assert_eq!(r2.to_bits(), -3_000_000_000_000_000_000);
let r3 = (-a).mul_with(b_neg, RoundingMode::HalfToEven);
assert_eq!(r3.to_bits(), 3_000_000_000_000_000_000);
}
#[test]
fn d18_div_negative_signs_at_s18() {
use decimal_scaled::D18;
let one = D18::<18>::from_int(1);
let three_pos = D18::<18>::from_int(3);
let three_neg = -three_pos;
let pos_pos = one.div_with(three_pos, RoundingMode::HalfToEven);
let pos_neg = one.div_with(three_neg, RoundingMode::HalfToEven);
let neg_pos = (-one).div_with(three_pos, RoundingMode::HalfToEven);
let neg_neg = (-one).div_with(three_neg, RoundingMode::HalfToEven);
assert_eq!(pos_neg.to_bits(), -pos_pos.to_bits());
assert_eq!(neg_pos.to_bits(), -pos_pos.to_bits());
assert_eq!(neg_neg.to_bits(), pos_pos.to_bits());
}
#[test]
fn d18_mul_half_to_even_tie_at_s18() {
use decimal_scaled::D18;
let a = D18::<18>::from_bits(1_000_000_000);
let b = D18::<18>::from_bits(2_500_000_000);
let r = a.mul_with(b, RoundingMode::HalfToEven);
assert_eq!(r.to_bits(), 2);
let a3 = D18::<18>::from_bits(1_000_000_000);
let b3 = D18::<18>::from_bits(3_500_000_000);
let r3 = a3.mul_with(b3, RoundingMode::HalfToEven);
assert_eq!(r3.to_bits(), 4);
}
#[test]
fn d18_mul_scale_0_short_circuit() {
use decimal_scaled::D18;
let a = D18::<0>::from_int(12_345);
let b = D18::<0>::from_int(67_890);
let r = a.mul_with(b, RoundingMode::HalfToEven);
assert_eq!(r.to_bits(), 12_345_i64 * 67_890);
}
#[test]
fn d18_div_with_at_s10_fast_path() {
use decimal_scaled::D18;
let a = D18::<10>::from_int(7);
let b = D18::<10>::from_int(2);
let r = a.div_with(b, RoundingMode::HalfToEven);
assert_eq!(r.to_bits(), 35_000_000_000);
}