mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
min,max: Add tests for signaling NaNs and update documentation
We do handle signaling NaNs properly, with the exception of raising exceptions as IEEE 754 requires. Add tests to this effect for `fmin`, `fminimum`, `fminimum_num`, and the max variants.
This commit is contained in:
@@ -77,9 +77,12 @@ pub fn fmaxf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
// Note that (YaN, sNaN) and (sNaN, YaN) results differ from 754-2008. This is intentional,
|
||||
// see comments in the generic implementations.
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
@@ -88,6 +91,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
@@ -95,6 +100,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -103,6 +110,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
@@ -111,6 +120,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
@@ -119,6 +130,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
@@ -127,6 +140,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -140,6 +155,18 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
@@ -147,12 +174,29 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
// Ordering between zeros does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -186,6 +230,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
@@ -193,6 +239,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -201,6 +249,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
@@ -209,6 +259,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
@@ -217,6 +269,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
@@ -225,6 +279,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -238,19 +294,54 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fmax({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
// Ordering between zeros
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -69,6 +69,7 @@ pub fn fmaximumf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
@@ -122,29 +123,63 @@ fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NAN, F::SNAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fminimum({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// On platforms where operations only return a single canonical NaN (e.g. RISC-V), the
|
||||
// result may not exactly match one of the inputs which is fine.
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::SNAN,).is_nan());
|
||||
assert!(f(F::ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ZERO,).is_nan());
|
||||
assert!(f(F::SNAN, F::ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::ZERO,).is_nan());
|
||||
assert!(f(F::ZERO, F::SNAN,).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -220,29 +255,63 @@ fn fmaximum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NAN, F::SNAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fmaximum({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// On platforms where operations only return a single canonical NaN (e.g. RISC-V), the
|
||||
// result may not exactly match one of the inputs which is fine.
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::SNAN,).is_nan());
|
||||
assert!(f(F::ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ZERO,).is_nan());
|
||||
assert!(f(F::SNAN, F::ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::ZERO,).is_nan());
|
||||
assert!(f(F::ZERO, F::SNAN,).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -69,6 +69,7 @@ pub fn fmaximum_numf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
@@ -81,6 +82,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
@@ -89,6 +92,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -97,6 +102,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
@@ -105,6 +112,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
@@ -113,6 +122,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
@@ -121,6 +132,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -134,17 +147,52 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
actual,
|
||||
expected,
|
||||
"fminimum_num({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -179,6 +227,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
@@ -187,6 +237,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -195,6 +247,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
@@ -203,6 +257,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
@@ -211,6 +267,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
@@ -219,6 +277,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -232,17 +292,52 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
actual,
|
||||
expected,
|
||||
"fmaximum_num({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
//! - Otherwise, either `x` or `y`, canonicalized
|
||||
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! We do not treat sNaN and qNaN differently; even though IEEE technically requires this, (a call
|
||||
//! with sNaN should return qNaN rather than the other result), it breaks associativity so isn't
|
||||
//! desired behavior. C23 does not differentiate between sNaN and qNaN, so we do not either. More
|
||||
//! on the problems with `minNum` [here][minnum-removal].
|
||||
//!
|
||||
//! More on the differences: [link].
|
||||
//! IEEE also specifies that a sNaN in either argument should signal invalid, but we do not
|
||||
//! implement this.
|
||||
//!
|
||||
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
//! [minnum-removal]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
//! - Otherwise, either `x` or `y`, canonicalized
|
||||
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! We do not treat sNaN and qNaN differently; even though IEEE technically requires this, (a call
|
||||
//! with sNaN should return qNaN rather than the other result), it breaks associativity so isn't
|
||||
//! desired behavior. C23 does not differentiate between sNaN and qNaN, so we do not either. More
|
||||
//! on the problems with `minNum` [here][minnum-removal].
|
||||
//!
|
||||
//! More on the differences: [link].
|
||||
//! IEEE also specifies that a sNaN in either argument should signal invalid, but we do not
|
||||
//! implement this.
|
||||
//!
|
||||
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
//! [minnum-removal]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user