After adding tests, the current implementation for fminimum fails when
provided a negative zero and NaN as inputs:
---- math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f64 stdout ----
thread 'math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f64' panicked at libm/src/math/fminimum_fmaximum_num.rs:240:13:
fmaximum_num(-0x0p+0, NaN)
l: NaN (0x7ff8000000000000)
r: -0.0 (0x8000000000000000)
---- math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f32 stdout ----
thread 'math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f32' panicked at libm/src/math/fminimum_fmaximum_num.rs:240:13:
fmaximum_num(-0x0p+0, NaN)
l: NaN (0x7fc00000)
r: -0.0 (0x80000000)
Add more thorough spec tests for these functions and correct the
implementations.
Canonicalization is also moved to a trait method to centralize
documentation about what it does and doesn't do.
160 lines
5.5 KiB
Rust
160 lines
5.5 KiB
Rust
/// `libm` cannot have dependencies, so this is vendored directly from the `cfg-if` crate
|
|
/// (with some comments stripped for compactness).
|
|
macro_rules! cfg_if {
|
|
// match if/else chains with a final `else`
|
|
($(
|
|
if #[cfg($meta:meta)] { $($tokens:tt)* }
|
|
) else * else {
|
|
$($tokens2:tt)*
|
|
}) => {
|
|
cfg_if! { @__items () ; $( ( ($meta) ($($tokens)*) ), )* ( () ($($tokens2)*) ), }
|
|
};
|
|
|
|
// match if/else chains lacking a final `else`
|
|
(
|
|
if #[cfg($i_met:meta)] { $($i_tokens:tt)* }
|
|
$( else if #[cfg($e_met:meta)] { $($e_tokens:tt)* } )*
|
|
) => {
|
|
cfg_if! {
|
|
@__items
|
|
() ;
|
|
( ($i_met) ($($i_tokens)*) ),
|
|
$( ( ($e_met) ($($e_tokens)*) ), )*
|
|
( () () ),
|
|
}
|
|
};
|
|
|
|
// Internal and recursive macro to emit all the items
|
|
//
|
|
// Collects all the negated cfgs in a list at the beginning and after the
|
|
// semicolon is all the remaining items
|
|
(@__items ($($not:meta,)*) ; ) => {};
|
|
(@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($tokens:tt)*) ), $($rest:tt)*) => {
|
|
#[cfg(all($($m,)* not(any($($not),*))))] cfg_if! { @__identity $($tokens)* }
|
|
cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* }
|
|
};
|
|
|
|
// Internal macro to make __apply work out right for different match types,
|
|
// because of how macros matching/expand stuff.
|
|
(@__identity $($tokens:tt)*) => { $($tokens)* };
|
|
}
|
|
|
|
/// Choose between using an arch-specific implementation and the function body. Returns directly
|
|
/// if the arch implementation is used, otherwise continue with the rest of the function.
|
|
///
|
|
/// Specify a `use_arch` meta field if an architecture-specific implementation is provided.
|
|
/// These live in the `math::arch::some_target_arch` module.
|
|
///
|
|
/// Specify a `use_arch_required` meta field if something architecture-specific must be used
|
|
/// regardless of feature configuration (`force-soft-floats`).
|
|
///
|
|
/// The passed meta options do not need to account for the `arch` target feature.
|
|
macro_rules! select_implementation {
|
|
(
|
|
name: $fn_name:ident,
|
|
// Configuration meta for when to use arch-specific implementation that requires hard
|
|
// float ops
|
|
$( use_arch: $use_arch:meta, )?
|
|
// Configuration meta for when to use the arch module regardless of whether softfloats
|
|
// have been requested.
|
|
$( use_arch_required: $use_arch_required:meta, )?
|
|
args: $($arg:ident),+ ,
|
|
) => {
|
|
// FIXME: these use paths that are a pretty fragile (`super`). We should figure out
|
|
// something better w.r.t. how this is vendored into compiler-builtins.
|
|
|
|
// However, we do need a few things from `arch` that are used even with soft floats.
|
|
select_implementation! {
|
|
@cfg $($use_arch_required)?;
|
|
if true {
|
|
return super::arch::$fn_name( $($arg),+ );
|
|
}
|
|
}
|
|
|
|
// By default, never use arch-specific implementations if we have force-soft-floats
|
|
#[cfg(arch_enabled)]
|
|
select_implementation! {
|
|
@cfg $($use_arch)?;
|
|
// Wrap in `if true` to avoid unused warnings
|
|
if true {
|
|
return super::arch::$fn_name( $($arg),+ );
|
|
}
|
|
}
|
|
};
|
|
|
|
// Coalesce helper to construct an expression only if a config is provided
|
|
(@cfg ; $ex:expr) => { };
|
|
(@cfg $provided:meta; $ex:expr) => { #[cfg($provided)] $ex };
|
|
}
|
|
|
|
/// Construct a 16-bit float from hex float representation (C-style), guaranteed to
|
|
/// evaluate at compile time.
|
|
#[cfg(f16_enabled)]
|
|
#[cfg_attr(feature = "unstable-public-internals", macro_export)]
|
|
#[allow(unused_macros)]
|
|
macro_rules! hf16 {
|
|
($s:literal) => {{
|
|
const X: f16 = $crate::support::hf16($s);
|
|
X
|
|
}};
|
|
}
|
|
|
|
/// Construct a 32-bit float from hex float representation (C-style), guaranteed to
|
|
/// evaluate at compile time.
|
|
#[allow(unused_macros)]
|
|
#[cfg_attr(feature = "unstable-public-internals", macro_export)]
|
|
macro_rules! hf32 {
|
|
($s:literal) => {{
|
|
const X: f32 = $crate::support::hf32($s);
|
|
X
|
|
}};
|
|
}
|
|
|
|
/// Construct a 64-bit float from hex float representation (C-style), guaranteed to
|
|
/// evaluate at compile time.
|
|
#[allow(unused_macros)]
|
|
#[cfg_attr(feature = "unstable-public-internals", macro_export)]
|
|
macro_rules! hf64 {
|
|
($s:literal) => {{
|
|
const X: f64 = $crate::support::hf64($s);
|
|
X
|
|
}};
|
|
}
|
|
|
|
/// Construct a 128-bit float from hex float representation (C-style), guaranteed to
|
|
/// evaluate at compile time.
|
|
#[cfg(f128_enabled)]
|
|
#[allow(unused_macros)]
|
|
#[cfg_attr(feature = "unstable-public-internals", macro_export)]
|
|
macro_rules! hf128 {
|
|
($s:literal) => {{
|
|
const X: f128 = $crate::support::hf128($s);
|
|
X
|
|
}};
|
|
}
|
|
|
|
/// Assert `F::biteq` with better messages.
|
|
#[cfg(test)]
|
|
macro_rules! assert_biteq {
|
|
($left:expr, $right:expr, $($tt:tt)*) => {{
|
|
let l = $left;
|
|
let r = $right;
|
|
// hack to get width from a value
|
|
let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits());
|
|
assert!(
|
|
$crate::support::Float::biteq(l, r),
|
|
"{}\nl: {l:?} ({lb:#0width$x} {lh})\nr: {r:?} ({rb:#0width$x} {rh})",
|
|
format_args!($($tt)*),
|
|
lb = l.to_bits(),
|
|
lh = $crate::support::Hexf(l),
|
|
rb = r.to_bits(),
|
|
rh = $crate::support::Hexf(r),
|
|
width = ((bits / 4) + 2) as usize,
|
|
|
|
);
|
|
}};
|
|
($left:expr, $right:expr $(,)?) => {
|
|
assert_biteq!($left, $right, "")
|
|
};
|
|
}
|