use f8::f8;
fn reference_bits(value: f64) -> u8 {
value.clamp(0.0, 255.0).round_ties_even() as u8
}
#[test]
#[cfg_attr(miri, ignore = "pure-integer exhaustive coverage is run natively")]
#[allow(clippy::op_ref)]
fn exhaustive_arithmetic_and_overloads_match_f64_reference() {
macro_rules! check {
($lhs:ident, $rhs:ident, $expected:expr, $method:ident, $op:tt, $assign:tt) => {{
let mut owned_assignment = $lhs;
owned_assignment $assign $rhs;
let mut borrowed_assignment = $lhs;
borrowed_assignment $assign &$rhs;
assert_eq!(
[
$lhs.$method($rhs),
$lhs $op $rhs,
$lhs $op &$rhs,
&$lhs $op $rhs,
&$lhs $op &$rhs,
owned_assignment,
borrowed_assignment,
],
[f8::from_bits($expected); 7],
"{}({}, {})",
stringify!($method),
$lhs.to_bits(),
$rhs.to_bits(),
);
}};
}
for a in 0..=u8::MAX {
for b in 0..=u8::MAX {
let lhs = f8::from_bits(a);
let rhs = f8::from_bits(b);
let left = f64::from(a);
let right = f64::from(b);
check!(lhs, rhs, reference_bits(left + right), saturating_add, +, +=);
check!(lhs, rhs, reference_bits(left - right), saturating_sub, -, -=);
check!(lhs, rhs, reference_bits(left * right / 255.0), saturating_mul, *, *=);
let quotient = if b == 0 {
if a == 0 { 0 } else { 255 }
} else {
reference_bits(left * 255.0 / right)
};
check!(lhs, rhs, quotient, saturating_div, /, /=);
}
}
}
#[test]
#[allow(clippy::eq_op)]
fn normalized_identities_hold_for_every_value() {
for bits in 0..=u8::MAX {
let value = f8::from_bits(bits);
assert_eq!(value + f8::ZERO, value);
assert_eq!(f8::ZERO + value, value);
assert_eq!(value + f8::ONE, f8::ONE);
assert_eq!(value - f8::ZERO, value);
assert_eq!(value - value, f8::ZERO);
assert_eq!(f8::ZERO - value, f8::ZERO);
assert_eq!(value * f8::ONE, value);
assert_eq!(f8::ONE * value, value);
assert_eq!(value * f8::ZERO, f8::ZERO);
assert_eq!(f8::ZERO * value, f8::ZERO);
assert_eq!(value / f8::ONE, value);
}
}
#[test]
#[allow(clippy::eq_op)]
fn zero_division_is_defined_and_positive_self_division_is_one() {
assert_eq!(f8::ZERO / f8::ZERO, f8::ZERO);
for bits in 1..=u8::MAX {
let value = f8::from_bits(bits);
assert_eq!(value / f8::ZERO, f8::ONE);
assert_eq!(f8::ZERO / value, f8::ZERO);
assert_eq!(value / value, f8::ONE);
assert_eq!(f8::ONE / value, f8::ONE);
}
}
#[test]
fn division_rounds_midpoints_to_even_in_both_directions() {
for (a, b, expected) in [(1, 2, 128), (1, 6, 42), (1, 10, 26), (3, 10, 76)] {
assert_eq!(
(f8::from_bits(a) / f8::from_bits(b)).to_bits(),
expected,
"{a} / {b}",
);
}
}
#[test]
fn empty_and_singleton_iterators_use_normalized_identities() {
assert_eq!(std::iter::empty::<f8>().sum::<f8>(), f8::ZERO);
assert_eq!(std::iter::empty::<&f8>().sum::<f8>(), f8::ZERO);
assert_eq!(std::iter::empty::<f8>().product::<f8>(), f8::ONE);
assert_eq!(std::iter::empty::<&f8>().product::<f8>(), f8::ONE);
for bits in 0..=u8::MAX {
let value = f8::from_bits(bits);
assert_eq!(std::iter::once(value).sum::<f8>(), value);
assert_eq!(std::iter::once(&value).sum::<f8>(), value);
assert_eq!(std::iter::once(value).product::<f8>(), value);
assert_eq!(std::iter::once(&value).product::<f8>(), value);
}
}
#[test]
fn iterator_sums_saturate() {
for (bits, expected) in [([20, 30, 40], 90), ([100, 100, 100], 255)] {
let values = bits.map(f8::from_bits);
assert_eq!(values.iter().copied().sum::<f8>().to_bits(), expected);
assert_eq!(values.iter().sum::<f8>().to_bits(), expected);
assert_eq!(values.iter().rev().sum::<f8>().to_bits(), expected);
}
}
#[test]
fn iterator_products_quantize_each_step_and_depend_on_order() {
let values = [1, 128, 128].map(f8::from_bits);
let forward = values.iter().copied().product::<f8>();
let reverse = values.iter().rev().copied().product::<f8>();
assert_eq!(forward.to_bits(), 1);
assert_eq!(reverse, f8::ZERO);
assert_eq!(values.iter().product::<f8>(), forward);
assert_eq!(values.iter().rev().product::<f8>(), reverse);
assert_eq!((values[0] * values[1]) * values[2], forward);
assert_eq!(values[0] * (values[1] * values[2]), reverse);
let unquantized_product = values
.iter()
.map(|value| f64::from(value.to_bits()) / 255.0)
.product::<f64>();
assert_eq!(reference_bits(unquantized_product * 255.0), 0);
assert_ne!(
forward.to_bits(),
reference_bits(unquantized_product * 255.0)
);
let with_zero = [f8::ONE, f8::from_bits(128), f8::ZERO, f8::ONE];
assert_eq!(with_zero.iter().copied().product::<f8>(), f8::ZERO);
assert_eq!(with_zero.iter().product::<f8>(), f8::ZERO);
}
#[test]
fn saturating_methods_are_const() {
const RESULTS: [f8; 9] = [
f8::from_bits(200).saturating_add(f8::from_bits(100)),
f8::from_bits(100).saturating_sub(f8::from_bits(200)),
f8::from_bits(128).saturating_mul(f8::from_bits(128)),
f8::ONE.saturating_mul(f8::ONE),
f8::from_bits(1).saturating_div(f8::from_bits(2)),
f8::from_bits(1).saturating_div(f8::from_bits(6)),
f8::ONE.saturating_div(f8::from_bits(128)),
f8::ZERO.saturating_div(f8::ZERO),
f8::ONE.saturating_div(f8::ZERO),
];
assert_eq!(
RESULTS.map(f8::to_bits),
[255, 0, 64, 255, 128, 42, 255, 0, 255]
);
}