| // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we |
| // override it internally back to the default frequency. |
| //@compile-flags: -Zmiri-provenance-gc=10000 |
| #![feature(float_gamma, portable_simd, core_intrinsics)] |
| use std::fmt; |
| use std::hint::black_box; |
| |
| #[path = "../utils/mod.rs"] |
| mod utils; |
| use utils::check_all_outcomes; |
| |
| fn ldexp(a: f64, b: i32) -> f64 { |
| extern "C" { |
| fn ldexp(x: f64, n: i32) -> f64; |
| } |
| unsafe { ldexp(a, b) } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| enum Sign { |
| Neg = 1, |
| Pos = 0, |
| } |
| use Sign::*; |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| enum NaNKind { |
| Quiet = 1, |
| Signaling = 0, |
| } |
| use NaNKind::*; |
| |
| // -- f32 support |
| #[repr(C)] |
| #[derive(Copy, Clone, Eq, PartialEq, Hash)] |
| struct F32(u32); |
| |
| impl From<f32> for F32 { |
| fn from(x: f32) -> Self { |
| F32(x.to_bits()) |
| } |
| } |
| |
| /// Returns a value that is `ones` many 1-bits. |
| fn u32_ones(ones: u32) -> u32 { |
| assert!(ones <= 32); |
| if ones == 0 { |
| // `>>` by 32 doesn't actually shift. So inconsistent :( |
| return 0; |
| } |
| u32::MAX >> (32 - ones) |
| } |
| |
| const F32_SIGN_BIT: u32 = 32 - 1; // position of the sign bit |
| const F32_EXP: u32 = 8; // 8 bits of exponent |
| const F32_MANTISSA: u32 = F32_SIGN_BIT - F32_EXP; |
| const F32_NAN_PAYLOAD: u32 = F32_MANTISSA - 1; |
| |
| impl fmt::Debug for F32 { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| // Alaways show raw bits. |
| write!(f, "0x{:08x} ", self.0)?; |
| // Also show nice version. |
| let val = self.0; |
| let sign = val >> F32_SIGN_BIT; |
| let val = val & u32_ones(F32_SIGN_BIT); // mask away sign |
| let exp = val >> F32_MANTISSA; |
| let mantissa = val & u32_ones(F32_MANTISSA); |
| if exp == u32_ones(F32_EXP) { |
| // A NaN! Special printing. |
| let sign = if sign != 0 { Neg } else { Pos }; |
| let quiet = if (mantissa >> F32_NAN_PAYLOAD) != 0 { Quiet } else { Signaling }; |
| let payload = mantissa & u32_ones(F32_NAN_PAYLOAD); |
| write!(f, "(NaN: {:?}, {:?}, payload = {:#x})", sign, quiet, payload) |
| } else { |
| // Normal float value. |
| write!(f, "({})", f32::from_bits(self.0)) |
| } |
| } |
| } |
| |
| impl F32 { |
| fn nan(sign: Sign, kind: NaNKind, payload: u32) -> Self { |
| // Either the quiet bit must be set of the payload must be non-0; |
| // otherwise this is not a NaN but an infinity. |
| assert!(kind == Quiet || payload != 0); |
| // Payload must fit in 22 bits. |
| assert!(payload < (1 << F32_NAN_PAYLOAD)); |
| // Concatenate the bits (with a 22bit payload). |
| // Pattern: [negative] ++ [1]^8 ++ [quiet] ++ [payload] |
| let val = ((sign as u32) << F32_SIGN_BIT) |
| | (u32_ones(F32_EXP) << F32_MANTISSA) |
| | ((kind as u32) << F32_NAN_PAYLOAD) |
| | payload; |
| // Sanity check. |
| assert!(f32::from_bits(val).is_nan()); |
| // Done! |
| F32(val) |
| } |
| |
| fn as_f32(self) -> f32 { |
| black_box(f32::from_bits(self.0)) |
| } |
| } |
| |
| // -- f64 support |
| #[repr(C)] |
| #[derive(Copy, Clone, Eq, PartialEq, Hash)] |
| struct F64(u64); |
| |
| impl From<f64> for F64 { |
| fn from(x: f64) -> Self { |
| F64(x.to_bits()) |
| } |
| } |
| |
| /// Returns a value that is `ones` many 1-bits. |
| fn u64_ones(ones: u32) -> u64 { |
| assert!(ones <= 64); |
| if ones == 0 { |
| // `>>` by 32 doesn't actually shift. So inconsistent :( |
| return 0; |
| } |
| u64::MAX >> (64 - ones) |
| } |
| |
| const F64_SIGN_BIT: u32 = 64 - 1; // position of the sign bit |
| const F64_EXP: u32 = 11; // 11 bits of exponent |
| const F64_MANTISSA: u32 = F64_SIGN_BIT - F64_EXP; |
| const F64_NAN_PAYLOAD: u32 = F64_MANTISSA - 1; |
| |
| impl fmt::Debug for F64 { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| // Alaways show raw bits. |
| write!(f, "0x{:08x} ", self.0)?; |
| // Also show nice version. |
| let val = self.0; |
| let sign = val >> F64_SIGN_BIT; |
| let val = val & u64_ones(F64_SIGN_BIT); // mask away sign |
| let exp = val >> F64_MANTISSA; |
| let mantissa = val & u64_ones(F64_MANTISSA); |
| if exp == u64_ones(F64_EXP) { |
| // A NaN! Special printing. |
| let sign = if sign != 0 { Neg } else { Pos }; |
| let quiet = if (mantissa >> F64_NAN_PAYLOAD) != 0 { Quiet } else { Signaling }; |
| let payload = mantissa & u64_ones(F64_NAN_PAYLOAD); |
| write!(f, "(NaN: {:?}, {:?}, payload = {:#x})", sign, quiet, payload) |
| } else { |
| // Normal float value. |
| write!(f, "({})", f64::from_bits(self.0)) |
| } |
| } |
| } |
| |
| impl F64 { |
| fn nan(sign: Sign, kind: NaNKind, payload: u64) -> Self { |
| // Either the quiet bit must be set of the payload must be non-0; |
| // otherwise this is not a NaN but an infinity. |
| assert!(kind == Quiet || payload != 0); |
| // Payload must fit in 52 bits. |
| assert!(payload < (1 << F64_NAN_PAYLOAD)); |
| // Concatenate the bits (with a 52bit payload). |
| // Pattern: [negative] ++ [1]^11 ++ [quiet] ++ [payload] |
| let val = ((sign as u64) << F64_SIGN_BIT) |
| | (u64_ones(F64_EXP) << F64_MANTISSA) |
| | ((kind as u64) << F64_NAN_PAYLOAD) |
| | payload; |
| // Sanity check. |
| assert!(f64::from_bits(val).is_nan()); |
| // Done! |
| F64(val) |
| } |
| |
| fn as_f64(self) -> f64 { |
| black_box(f64::from_bits(self.0)) |
| } |
| } |
| |
| // -- actual tests |
| |
| fn test_f32() { |
| // Freshly generated NaNs can have either sign. |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(0.0 / black_box(0.0)) |
| }); |
| // When there are NaN inputs, their payload can be propagated, with any sign. |
| let all1_payload = u32_ones(22); |
| let all1 = F32::nan(Pos, Quiet, all1_payload).as_f32(); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, all1_payload), |
| F32::nan(Neg, Quiet, all1_payload), |
| ], |
| || F32::from(0.0 + all1), |
| ); |
| // When there are two NaN inputs, the output can be either one, or the preferred NaN. |
| let just1 = F32::nan(Neg, Quiet, 1).as_f32(); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, 1), |
| F32::nan(Neg, Quiet, 1), |
| F32::nan(Pos, Quiet, all1_payload), |
| F32::nan(Neg, Quiet, all1_payload), |
| ], |
| || F32::from(just1 - all1), |
| ); |
| // When there are *signaling* NaN inputs, they might be quieted or not. |
| let all1_snan = F32::nan(Pos, Signaling, all1_payload).as_f32(); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, all1_payload), |
| F32::nan(Neg, Quiet, all1_payload), |
| F32::nan(Pos, Signaling, all1_payload), |
| F32::nan(Neg, Signaling, all1_payload), |
| ], |
| || F32::from(0.0 * all1_snan), |
| ); |
| // Mix signaling and non-signaling NaN. |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, 1), |
| F32::nan(Neg, Quiet, 1), |
| F32::nan(Pos, Quiet, all1_payload), |
| F32::nan(Neg, Quiet, all1_payload), |
| F32::nan(Pos, Signaling, all1_payload), |
| F32::nan(Neg, Signaling, all1_payload), |
| ], |
| || F32::from(just1 % all1_snan), |
| ); |
| |
| // Unary `-` must preserve payloads exactly. |
| check_all_outcomes([F32::nan(Neg, Quiet, all1_payload)], || F32::from(-all1)); |
| check_all_outcomes([F32::nan(Neg, Signaling, all1_payload)], || F32::from(-all1_snan)); |
| |
| // Intrinsics |
| let nan = F32::nan(Neg, Quiet, 0).as_f32(); |
| let snan = F32::nan(Neg, Signaling, 1).as_f32(); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(f32::min(nan, nan)) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.floor()) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || F32::from(nan.sin())); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, 1), |
| F32::nan(Neg, Quiet, 1), |
| F32::nan(Pos, Quiet, 2), |
| F32::nan(Neg, Quiet, 2), |
| F32::nan(Pos, Quiet, all1_payload), |
| F32::nan(Neg, Quiet, all1_payload), |
| F32::nan(Pos, Signaling, all1_payload), |
| F32::nan(Neg, Signaling, all1_payload), |
| ], |
| || F32::from(just1.mul_add(F32::nan(Neg, Quiet, 2).as_f32(), all1_snan)), |
| ); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.powf(nan)) |
| }); |
| check_all_outcomes( |
| [1.0f32.into()], |
| || F32::from(1.0f32.powf(nan)), // special `pow` rule |
| ); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.powi(1)) |
| }); |
| |
| // libm functions |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.sinh()) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.atan2(nan)) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(nan.ln_gamma().0) |
| }); |
| check_all_outcomes( |
| [ |
| F32::from(1.0), |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, 1), |
| F32::nan(Neg, Quiet, 1), |
| F32::nan(Pos, Signaling, 1), |
| F32::nan(Neg, Signaling, 1), |
| ], |
| || F32::from(snan.powf(0.0)), |
| ); |
| } |
| |
| fn test_f64() { |
| // Freshly generated NaNs can have either sign. |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(0.0 / black_box(0.0)) |
| }); |
| // When there are NaN inputs, their payload can be propagated, with any sign. |
| let all1_payload = u64_ones(51); |
| let all1 = F64::nan(Pos, Quiet, all1_payload).as_f64(); |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, all1_payload), |
| F64::nan(Neg, Quiet, all1_payload), |
| ], |
| || F64::from(0.0 + all1), |
| ); |
| // When there are two NaN inputs, the output can be either one, or the preferred NaN. |
| let just1 = F64::nan(Neg, Quiet, 1).as_f64(); |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, 1), |
| F64::nan(Neg, Quiet, 1), |
| F64::nan(Pos, Quiet, all1_payload), |
| F64::nan(Neg, Quiet, all1_payload), |
| ], |
| || F64::from(just1 - all1), |
| ); |
| // When there are *signaling* NaN inputs, they might be quieted or not. |
| let all1_snan = F64::nan(Pos, Signaling, all1_payload).as_f64(); |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, all1_payload), |
| F64::nan(Neg, Quiet, all1_payload), |
| F64::nan(Pos, Signaling, all1_payload), |
| F64::nan(Neg, Signaling, all1_payload), |
| ], |
| || F64::from(0.0 * all1_snan), |
| ); |
| // Mix signaling and non-signaling NaN. |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, 1), |
| F64::nan(Neg, Quiet, 1), |
| F64::nan(Pos, Quiet, all1_payload), |
| F64::nan(Neg, Quiet, all1_payload), |
| F64::nan(Pos, Signaling, all1_payload), |
| F64::nan(Neg, Signaling, all1_payload), |
| ], |
| || F64::from(just1 % all1_snan), |
| ); |
| |
| // Intrinsics |
| let nan = F64::nan(Neg, Quiet, 0).as_f64(); |
| let snan = F64::nan(Neg, Signaling, 1).as_f64(); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(f64::min(nan, nan)) |
| }); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.floor()) |
| }); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || F64::from(nan.sin())); |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, 1), |
| F64::nan(Neg, Quiet, 1), |
| F64::nan(Pos, Quiet, 2), |
| F64::nan(Neg, Quiet, 2), |
| F64::nan(Pos, Quiet, all1_payload), |
| F64::nan(Neg, Quiet, all1_payload), |
| F64::nan(Pos, Signaling, all1_payload), |
| F64::nan(Neg, Signaling, all1_payload), |
| ], |
| || F64::from(just1.mul_add(F64::nan(Neg, Quiet, 2).as_f64(), all1_snan)), |
| ); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.powf(nan)) |
| }); |
| check_all_outcomes( |
| [1.0f64.into()], |
| || F64::from(1.0f64.powf(nan)), // special `pow` rule |
| ); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.powi(1)) |
| }); |
| |
| // libm functions |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.sinh()) |
| }); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.atan2(nan)) |
| }); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(ldexp(nan, 1)) |
| }); |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(nan.ln_gamma().0) |
| }); |
| check_all_outcomes( |
| [ |
| F64::from(1.0), |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, 1), |
| F64::nan(Neg, Quiet, 1), |
| F64::nan(Pos, Signaling, 1), |
| F64::nan(Neg, Signaling, 1), |
| ], |
| || F64::from(snan.powf(0.0)), |
| ); |
| } |
| |
| fn test_casts() { |
| let all1_payload_32 = u32_ones(22); |
| let all1_payload_64 = u64_ones(51); |
| let left1_payload_64 = (all1_payload_32 as u64) << (51 - 22); |
| |
| // 64-to-32 |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32) |
| }); |
| // The preferred payload is always a possibility. |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, all1_payload_32), |
| F32::nan(Neg, Quiet, all1_payload_32), |
| ], |
| || F32::from(F64::nan(Pos, Quiet, all1_payload_64).as_f64() as f32), |
| ); |
| // If the input is signaling, then the output *may* also be signaling. |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, all1_payload_32), |
| F32::nan(Neg, Quiet, all1_payload_32), |
| F32::nan(Pos, Signaling, all1_payload_32), |
| F32::nan(Neg, Signaling, all1_payload_32), |
| ], |
| || F32::from(F64::nan(Pos, Signaling, all1_payload_64).as_f64() as f32), |
| ); |
| // Check that the low bits are gone (not the high bits). |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32) |
| }); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| F32::nan(Pos, Quiet, 1), |
| F32::nan(Neg, Quiet, 1), |
| ], |
| || F32::from(F64::nan(Pos, Quiet, 1 << (51 - 22)).as_f64() as f32), |
| ); |
| check_all_outcomes( |
| [ |
| F32::nan(Pos, Quiet, 0), |
| F32::nan(Neg, Quiet, 0), |
| // The `1` payload becomes `0`, and the `0` payload cannot be signaling, |
| // so these are the only options. |
| ], |
| || F32::from(F64::nan(Pos, Signaling, 1).as_f64() as f32), |
| ); |
| |
| // 32-to-64 |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64) |
| }); |
| // The preferred payload is always a possibility. |
| // Also checks that 0s are added on the right. |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, left1_payload_64), |
| F64::nan(Neg, Quiet, left1_payload_64), |
| ], |
| || F64::from(F32::nan(Pos, Quiet, all1_payload_32).as_f32() as f64), |
| ); |
| // If the input is signaling, then the output *may* also be signaling. |
| check_all_outcomes( |
| [ |
| F64::nan(Pos, Quiet, 0), |
| F64::nan(Neg, Quiet, 0), |
| F64::nan(Pos, Quiet, left1_payload_64), |
| F64::nan(Neg, Quiet, left1_payload_64), |
| F64::nan(Pos, Signaling, left1_payload_64), |
| F64::nan(Neg, Signaling, left1_payload_64), |
| ], |
| || F64::from(F32::nan(Pos, Signaling, all1_payload_32).as_f32() as f64), |
| ); |
| } |
| |
| fn test_simd() { |
| use std::intrinsics::simd::*; |
| use std::simd::*; |
| |
| let nan = F32::nan(Neg, Quiet, 0).as_f32(); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0]) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]) |
| }); |
| check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { |
| F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]) |
| }); |
| |
| // Casts |
| check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { |
| F64::from(unsafe { simd_cast::<f32x4, f64x4>(f32x4::splat(nan)) }[0]) |
| }); |
| } |
| |
| fn main() { |
| // Check our constants against std, just to be sure. |
| // We add 1 since our numbers are the number of bits stored |
| // to represent the value, and std has the precision of the value, |
| // which is one more due to the implicit leading 1. |
| assert_eq!(F32_MANTISSA + 1, f32::MANTISSA_DIGITS); |
| assert_eq!(F64_MANTISSA + 1, f64::MANTISSA_DIGITS); |
| |
| test_f32(); |
| test_f64(); |
| test_casts(); |
| test_simd(); |
| } |