#![allow(clippy::inline_always)]
#[cfg(all(not(feature = "std"), not(feature = "libm")))]
compile_error!(
"feature `xpath` without `std` requires feature `libm`: XPath needs \
floor/ceil/trunc, which core does not provide"
);
macro_rules! float_fn {
($(#[$m:meta])* $name:ident, $libm:ident) => {
$(#[$m])*
#[inline(always)]
#[must_use]
pub(crate) fn $name(x: f64) -> f64 {
#[cfg(feature = "std")]
{ x.$name() }
#[cfg(all(not(feature = "std"), feature = "libm"))]
{ libm::$libm(x) }
#[cfg(all(not(feature = "std"), not(feature = "libm")))]
{ let _ = x; unreachable!() }
}
};
}
float_fn!(
floor, floor
);
float_fn!(
ceil, ceil
);
float_fn!(
trunc, trunc
);
#[cfg(test)]
mod tests {
use super::*;
#[cfg(all(feature = "std", feature = "libm"))]
#[test]
fn libm_matches_std_bit_for_bit() {
let cases = [
0.0,
-0.0,
1.0,
-1.0,
0.5,
-0.5,
1.5,
-1.5,
2.5,
-2.5,
1e21,
-1e21,
1e-21,
0.1,
17.49,
-17.49,
1e15,
4.5,
-4.5,
123.456,
-123.456,
f64::MAX,
f64::MIN,
f64::MIN_POSITIVE,
];
for x in cases {
assert_eq!(
libm::floor(x).to_bits(),
x.floor().to_bits(),
"floor({x})"
);
assert_eq!(
libm::ceil(x).to_bits(),
x.ceil().to_bits(),
"ceil({x})"
);
assert_eq!(
libm::trunc(x).to_bits(),
x.trunc().to_bits(),
"trunc({x})"
);
}
}
#[test]
fn non_finite_inputs_propagate_rather_than_trapping() {
for x in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
assert_eq!(floor(x).is_nan(), x.is_nan(), "floor({x})");
assert_eq!(ceil(x).is_nan(), x.is_nan(), "ceil({x})");
assert_eq!(trunc(x).is_nan(), x.is_nan(), "trunc({x})");
}
assert!(floor(f64::INFINITY).is_infinite());
assert!(trunc(f64::NEG_INFINITY).is_infinite());
}
}