| use core::{fmt, mem, ops}; | |
| use super::int_traits::{CastFrom, Int, MinInt}; | |
| use crate::support::{DInt, DisplayHex}; | |
| /// Wrapper to extract the integer type half of the float's size | |
| #[allow(unused)] // only used in c-b | |
| pub type HalfRep<F> = <<F as Float>::Int as DInt>::H; | |
| /// Whether MIPS sNaN/qNaNs should be used. | |
| /// | |
| /// Note that MIPS is doing some general migration here, though this is only available on (rare) | |
| /// modern MIPS hardware per discussion at <https://github.com/WebAssembly/design/issues/976>. | |
| const MIPS_NAN: bool = cfg!(target_arch = "mips") || cfg!(target_arch = "mips64"); | |
| /// Trait for some basic operations on floats | |
| // #[allow(dead_code)] | |
| #[allow(dead_code)] // Some constants are only used with tests | |
| pub trait Float: | |
| Copy | |
| + fmt::Debug | |
| + DisplayHex | |
| + PartialEq | |
| + PartialOrd | |
| + ops::AddAssign | |
| + ops::MulAssign | |
| + ops::Add<Output = Self> | |
| + ops::Sub<Output = Self> | |
| + ops::Mul<Output = Self> | |
| + ops::Div<Output = Self> | |
| + ops::Rem<Output = Self> | |
| + ops::Neg<Output = Self> | |
| + 'static | |
| { | |
| /// A uint of the same width as the float | |
| type Int: Int<OtherSign = Self::SignedInt, Unsigned = Self::Int>; | |
| /// A int of the same width as the float | |
| type SignedInt: Int | |
| + MinInt<OtherSign = Self::Int, Unsigned = Self::Int> | |
| + ops::Neg<Output = Self::SignedInt>; | |
| const ZERO: Self; | |
| const NEG_ZERO: Self; | |
| const ONE: Self; | |
| const NEG_ONE: Self; | |
| const INFINITY: Self; | |
| const NEG_INFINITY: Self; | |
| const MAX: Self; | |
| const MIN: Self; | |
| /// Quiet NaN. | |
| const NAN: Self; | |
| /// Signaling NaN. | |
| const SNAN: Self; | |
| const NEG_NAN: Self; | |
| const NEG_SNAN: Self; | |
| const EPSILON: Self; | |
| const PI: Self; | |
| const NEG_PI: Self; | |
| const FRAC_PI_2: Self; | |
| const MIN_POSITIVE_NORMAL: Self; | |
| const MIN_POSITIVE_SUBNORMAL: Self; | |
| /// The bitwidth of the float type. | |
| const BITS: u32; | |
| /// The bitwidth of the significand (does not include the implicit bit). | |
| const SIG_BITS: u32; | |
| /// The bitwidth of the exponent. | |
| const EXP_BITS: u32 = Self::BITS - Self::SIG_BITS - 1; | |
| /// The saturated (maximum bitpattern) value of the exponent, i.e. the infinite | |
| /// representation. | |
| /// | |
| /// This shifted fully right, use `EXP_MASK` for the shifted value. | |
| const EXP_SAT: u32 = (1 << Self::EXP_BITS) - 1; | |
| /// The exponent bias value. | |
| const EXP_BIAS: u32 = Self::EXP_SAT >> 1; | |
| /// Maximum unbiased exponent value. | |
| const EXP_MAX: i32 = Self::EXP_BIAS as i32; | |
| /// Minimum *NORMAL* unbiased exponent value. | |
| const EXP_MIN: i32 = -(Self::EXP_MAX - 1); | |
| /// Minimum subnormal exponent value. | |
| const EXP_MIN_SUBNORM: i32 = Self::EXP_MIN - Self::SIG_BITS as i32; | |
| /// A mask for the sign bit | |
| const SIGN_MASK: Self::Int; | |
| /// A mask for the significand | |
| const SIG_MASK: Self::Int; | |
| /// A mask for the exponent | |
| const EXP_MASK: Self::Int; | |
| /// The implicit bit of the float format | |
| const IMPLICIT_BIT: Self::Int; | |
| /// A mask for the top bit of the significand, useful for NaN ops. | |
| const SIG_TOP_BIT: Self::Int; | |
| /// Returns `self` transmuted to `Self::Int` | |
| fn to_bits(self) -> Self::Int; | |
| /// Returns `self` transmuted to `Self::SignedInt` | |
| #[allow(dead_code)] | |
| fn to_bits_signed(self) -> Self::SignedInt { | |
| self.to_bits().signed() | |
| } | |
| /// Check bitwise equality. | |
| #[allow(dead_code)] | |
| fn biteq(self, rhs: Self) -> bool { | |
| self.to_bits() == rhs.to_bits() | |
| } | |
| /// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be | |
| /// represented in multiple different ways. | |
| /// | |
| /// This method returns `true` if two NaNs are compared. Use [`biteq`](Self::biteq) instead | |
| /// if `NaN` should not be treated separately. | |
| #[allow(dead_code)] | |
| fn eq_repr(self, rhs: Self) -> bool; | |
| /// Returns true if the value is NaN. | |
| fn is_nan(self) -> bool; | |
| fn is_qnan(self) -> bool { | |
| if !self.is_nan() { | |
| return false; | |
| } | |
| let top = self.to_bits() & Self::SIG_TOP_BIT; | |
| if MIPS_NAN { | |
| top == Self::Int::ZERO | |
| } else { | |
| top != Self::Int::ZERO | |
| } | |
| } | |
| fn is_snan(self) -> bool { | |
| self.is_nan() && !self.is_qnan() | |
| } | |
| /// Returns true if the value is +inf or -inf. | |
| fn is_infinite(self) -> bool; | |
| /// Returns true if the sign is negative. Extracts the sign bit regardless of zero or NaN. | |
| fn is_sign_negative(self) -> bool; | |
| /// Returns true if the sign is positive. Extracts the sign bit regardless of zero or NaN. | |
| fn is_sign_positive(self) -> bool { | |
| !self.is_sign_negative() | |
| } | |
| /// Returns if `self` is subnormal. | |
| #[allow(dead_code)] | |
| fn is_subnormal(self) -> bool { | |
| (self.to_bits() & Self::EXP_MASK) == Self::Int::ZERO | |
| } | |
| /// Returns the exponent, not adjusting for bias, not accounting for subnormals or zero. | |
| fn ex(self) -> u32 { | |
| u32::cast_from(self.to_bits() >> Self::SIG_BITS) & Self::EXP_SAT | |
| } | |
| /// Extract the exponent and adjust it for bias, not accounting for subnormals or zero. | |
| fn exp_unbiased(self) -> i32 { | |
| self.ex().signed() - (Self::EXP_BIAS as i32) | |
| } | |
| /// Returns the significand with no implicit bit (or the "fractional" part) | |
| #[allow(dead_code)] | |
| fn frac(self) -> Self::Int { | |
| self.to_bits() & Self::SIG_MASK | |
| } | |
| /// Returns a `Self::Int` transmuted back to `Self` | |
| fn from_bits(a: Self::Int) -> Self; | |
| /// Constructs a `Self` from its parts. Inputs are treated as bits and shifted into position. | |
| fn from_parts(negative: bool, exponent: u32, significand: Self::Int) -> Self { | |
| let sign = if negative { | |
| Self::Int::ONE | |
| } else { | |
| Self::Int::ZERO | |
| }; | |
| Self::from_bits( | |
| (sign << (Self::BITS - 1)) | |
| | (Self::Int::cast_from(exponent & Self::EXP_SAT) << Self::SIG_BITS) | |
| | (significand & Self::SIG_MASK), | |
| ) | |
| } | |
| #[allow(dead_code)] | |
| fn abs(self) -> Self; | |
| /// Returns a number composed of the magnitude of self and the sign of sign. | |
| fn copysign(self, other: Self) -> Self; | |
| /// Fused multiply add, rounding once. | |
| fn fma(self, y: Self, z: Self) -> Self; | |
| /// Returns (normalized exponent, normalized significand) | |
| fn normalize(significand: Self::Int) -> (i32, Self::Int); | |
| /// Returns a number that represents the sign of self. | |
| #[allow(dead_code)] | |
| fn signum(self) -> Self { | |
| if self.is_nan() { | |
| self | |
| } else { | |
| Self::ONE.copysign(self) | |
| } | |
| } | |
| /// Make a best-effort attempt to canonicalize the number. Note that this is allowed | |
| /// to be a nop and does not always quiet sNaNs. | |
| fn canonicalize(self) -> Self { | |
| // FIXME: LLVM often removes this. We should determine whether we can remove the operation, | |
| // or switch to something based on `llvm.canonicalize` (which has crashes, | |
| // <https://github.com/llvm/llvm-project/issues/32650>). | |
| self * Self::ONE | |
| } | |
| } | |
| /// Access the associated `Int` type from a float (helper to avoid ambiguous associated types). | |
| pub type IntTy<F> = <F as Float>::Int; | |
| macro_rules! float_impl { | |
| ( | |
| $ty:ident, | |
| $ity:ident, | |
| $sity:ident, | |
| $bits:expr, | |
| $significand_bits:expr, | |
| $from_bits:path, | |
| $to_bits:path, | |
| $fma_fn:ident, | |
| $fma_intrinsic:ident | |
| ) => { | |
| impl Float for $ty { | |
| type Int = $ity; | |
| type SignedInt = $sity; | |
| const ZERO: Self = 0.0; | |
| const NEG_ZERO: Self = -0.0; | |
| const ONE: Self = 1.0; | |
| const NEG_ONE: Self = -1.0; | |
| const INFINITY: Self = Self::INFINITY; | |
| const NEG_INFINITY: Self = Self::NEG_INFINITY; | |
| const MAX: Self = -Self::MIN; | |
| // Sign bit set, saturated mantissa, saturated exponent with last bit zeroed | |
| const MIN: Self = $from_bits(Self::Int::MAX & !(1 << Self::SIG_BITS)); | |
| // The default NaN seems to set one of the top two significand bits on most | |
| // platforms (sNaN vs. qNaN). For mips, the significand is all 1s (with the | |
| // exception of the signaling bit). | |
| const NAN: Self = $from_bits(if MIPS_NAN { | |
| Self::EXP_MASK | (Self::SIG_TOP_BIT - 1) | |
| } else { | |
| Self::EXP_MASK | Self::SIG_TOP_BIT | |
| }); | |
| const SNAN: Self = $from_bits(if MIPS_NAN { | |
| Self::EXP_MASK | Self::SIG_MASK | |
| } else { | |
| Self::EXP_MASK | (Self::SIG_TOP_BIT >> 1) | |
| }); | |
| // NAN isn't guaranteed to be positive but it usually is. We only use these for | |
| // tests. | |
| const NEG_NAN: Self = $from_bits($to_bits(Self::NAN) | Self::SIGN_MASK); | |
| const NEG_SNAN: Self = $from_bits($to_bits(Self::SNAN) | Self::SIGN_MASK); | |
| const EPSILON: Self = <$ty>::EPSILON; | |
| // Exponent is a 1 in the LSB | |
| const MIN_POSITIVE_NORMAL: Self = $from_bits(1 << Self::SIG_BITS); | |
| // Single 1 in the LSB | |
| const MIN_POSITIVE_SUBNORMAL: Self = $from_bits(1); | |
| const PI: Self = core::$ty::consts::PI; | |
| const NEG_PI: Self = -Self::PI; | |
| const FRAC_PI_2: Self = core::$ty::consts::FRAC_PI_2; | |
| const BITS: u32 = $bits; | |
| const SIG_BITS: u32 = $significand_bits; | |
| const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1); | |
| const SIG_MASK: Self::Int = (1 << Self::SIG_BITS) - 1; | |
| const EXP_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIG_MASK); | |
| const IMPLICIT_BIT: Self::Int = 1 << Self::SIG_BITS; | |
| const SIG_TOP_BIT: Self::Int = Self::IMPLICIT_BIT >> 1; | |
| fn to_bits(self) -> Self::Int { | |
| self.to_bits() | |
| } | |
| fn eq_repr(self, rhs: Self) -> bool { | |
| #[cfg(not(feature = "unmangled-names"))] | |
| fn is_nan(x: $ty) -> bool { | |
| // When not using unmangled-names, the "real" compiler-builtins might not have | |
| // the necessary builtin (__unordtf2) to test whether `f128` is NaN. | |
| // FIXME(f128): Remove once the nightly toolchain has the __unordtf2 builtin | |
| // x is NaN if all the bits of the exponent are set and the significand is non-0 | |
| x.to_bits() & $ty::EXP_MASK == $ty::EXP_MASK && x.to_bits() & $ty::SIG_MASK != 0 | |
| } | |
| #[cfg(feature = "unmangled-names")] | |
| fn is_nan(x: $ty) -> bool { | |
| x.is_nan() | |
| } | |
| if is_nan(self) && is_nan(rhs) { | |
| true | |
| } else { | |
| self.to_bits() == rhs.to_bits() | |
| } | |
| } | |
| fn is_nan(self) -> bool { | |
| self.is_nan() | |
| } | |
| fn is_infinite(self) -> bool { | |
| self.is_infinite() | |
| } | |
| fn is_sign_negative(self) -> bool { | |
| self.is_sign_negative() | |
| } | |
| fn from_bits(a: Self::Int) -> Self { | |
| Self::from_bits(a) | |
| } | |
| fn abs(self) -> Self { | |
| cfg_if! { | |
| // FIXME(msrv): `abs` is available in `core` starting with 1.85. | |
| if #[cfg(intrinsics_enabled)] { | |
| self.abs() | |
| } else { | |
| super::super::generic::fabs(self) | |
| } | |
| } | |
| } | |
| fn copysign(self, other: Self) -> Self { | |
| cfg_if! { | |
| // FIXME(msrv): `copysign` is available in `core` starting with 1.85. | |
| if #[cfg(intrinsics_enabled)] { | |
| self.copysign(other) | |
| } else { | |
| super::super::generic::copysign(self, other) | |
| } | |
| } | |
| } | |
| fn fma(self, y: Self, z: Self) -> Self { | |
| cfg_if! { | |
| // fma is not yet available in `core` | |
| if #[cfg(intrinsics_enabled)] { | |
| core::intrinsics::$fma_intrinsic(self, y, z) | |
| } else { | |
| super::super::$fma_fn(self, y, z) | |
| } | |
| } | |
| } | |
| fn normalize(significand: Self::Int) -> (i32, Self::Int) { | |
| let shift = significand.leading_zeros().wrapping_sub(Self::EXP_BITS); | |
| (1i32.wrapping_sub(shift as i32), significand << shift) | |
| } | |
| } | |
| }; | |
| } | |
| #[cfg(f16_enabled)] | |
| float_impl!( | |
| f16, | |
| u16, | |
| i16, | |
| 16, | |
| 10, | |
| f16::from_bits, | |
| f16::to_bits, | |
| fmaf16, | |
| fmaf16 | |
| ); | |
| float_impl!( | |
| f32, | |
| u32, | |
| i32, | |
| 32, | |
| 23, | |
| f32_from_bits, | |
| f32_to_bits, | |
| fmaf, | |
| fmaf32 | |
| ); | |
| float_impl!( | |
| f64, | |
| u64, | |
| i64, | |
| 64, | |
| 52, | |
| f64_from_bits, | |
| f64_to_bits, | |
| fma, | |
| fmaf64 | |
| ); | |
| #[cfg(f128_enabled)] | |
| float_impl!( | |
| f128, | |
| u128, | |
| i128, | |
| 128, | |
| 112, | |
| f128::from_bits, | |
| f128::to_bits, | |
| fmaf128, | |
| fmaf128 | |
| ); | |
| /* FIXME(msrv): vendor some things that are not const stable at our MSRV */ | |
| /// `f32::from_bits` | |
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | |
| pub const fn f32_from_bits(bits: u32) -> f32 { | |
| // SAFETY: POD cast with no preconditions | |
| unsafe { mem::transmute::<u32, f32>(bits) } | |
| } | |
| /// `f32::to_bits` | |
| #[allow(dead_code)] // workaround for false positive RUST-144060 | |
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | |
| pub const fn f32_to_bits(x: f32) -> u32 { | |
| // SAFETY: POD cast with no preconditions | |
| unsafe { mem::transmute::<f32, u32>(x) } | |
| } | |
| /// `f64::from_bits` | |
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | |
| pub const fn f64_from_bits(bits: u64) -> f64 { | |
| // SAFETY: POD cast with no preconditions | |
| unsafe { mem::transmute::<u64, f64>(bits) } | |
| } | |
| /// `f64::to_bits` | |
| #[allow(dead_code)] // workaround for false positive RUST-144060 | |
| #[allow(unnecessary_transmutes)] // lint appears in newer versions of Rust | |
| pub const fn f64_to_bits(x: f64) -> u64 { | |
| // SAFETY: POD cast with no preconditions | |
| unsafe { mem::transmute::<f64, u64>(x) } | |
| } | |
| /// Trait for floats twice the bit width of another integer. | |
| pub trait DFloat: Float { | |
| /// Float that is half the bit width of the floatthis trait is implemented for. | |
| type H: HFloat<D = Self>; | |
| /// Narrow the float type. | |
| fn narrow(self) -> Self::H; | |
| } | |
| /// Trait for floats half the bit width of another float. | |
| pub trait HFloat: Float { | |
| /// Float that is double the bit width of the float this trait is implemented for. | |
| type D: DFloat<H = Self>; | |
| /// Widen the float type. | |
| fn widen(self) -> Self::D; | |
| } | |
| macro_rules! impl_d_float { | |
| ($($X:ident $D:ident),*) => { | |
| $( | |
| impl DFloat for $D { | |
| type H = $X; | |
| fn narrow(self) -> Self::H { | |
| self as $X | |
| } | |
| } | |
| )* | |
| }; | |
| } | |
| macro_rules! impl_h_float { | |
| ($($H:ident $X:ident),*) => { | |
| $( | |
| impl HFloat for $H { | |
| type D = $X; | |
| fn widen(self) -> Self::D { | |
| self as $X | |
| } | |
| } | |
| )* | |
| }; | |
| } | |
| impl_d_float!(f32 f64); | |
| #[cfg(f16_enabled)] | |
| impl_d_float!(f16 f32); | |
| #[cfg(f128_enabled)] | |
| impl_d_float!(f64 f128); | |
| impl_h_float!(f32 f64); | |
| #[cfg(f16_enabled)] | |
| impl_h_float!(f16 f32); | |
| #[cfg(f128_enabled)] | |
| impl_h_float!(f64 f128); | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| #[cfg(f16_enabled)] | |
| fn check_f16() { | |
| // Constants | |
| assert_eq!(f16::EXP_SAT, 0b11111); | |
| assert_eq!(f16::EXP_BIAS, 15); | |
| assert_eq!(f16::EXP_MAX, 15); | |
| assert_eq!(f16::EXP_MIN, -14); | |
| assert_eq!(f16::EXP_MIN_SUBNORM, -24); | |
| assert_biteq!(f16::NAN, <f16 as Float>::NAN); | |
| // Value of NAN and FLT16_SNAN in C. We don't strictly need to match up, but it is good to | |
| // be aware if there are platforms where we don't. | |
| if MIPS_NAN { | |
| assert_biteq!(f16::NAN, f16::from_bits(0x7fbf)); | |
| assert_biteq!(f16::SNAN, f16::from_bits(0x7fff)); | |
| } else { | |
| assert_biteq!(f16::NAN, f16::from_bits(0x7e00)); | |
| assert_biteq!(f16::SNAN, f16::from_bits(0x7d00)); | |
| } | |
| assert!(f16::NAN.is_qnan()); | |
| // `exp_unbiased` | |
| assert_eq!(f16::FRAC_PI_2.exp_unbiased(), 0); | |
| assert_eq!((1.0f16 / 2.0).exp_unbiased(), -1); | |
| assert_eq!(f16::MAX.exp_unbiased(), 15); | |
| assert_eq!(f16::MIN.exp_unbiased(), 15); | |
| assert_eq!(f16::MIN_POSITIVE.exp_unbiased(), -14); | |
| // This is a convenience method and not ldexp, `exp_unbiased` does not return correct | |
| // results for zero and subnormals. | |
| assert_eq!(f16::ZERO.exp_unbiased(), -15); | |
| assert_eq!(f16::from_bits(0x1).exp_unbiased(), -15); | |
| assert_eq!(f16::MIN_POSITIVE, f16::MIN_POSITIVE_NORMAL); | |
| // `from_parts` | |
| assert_biteq!(f16::from_parts(true, f16::EXP_BIAS, 0), -1.0f16); | |
| assert_biteq!(f16::from_parts(false, 0, 1), f16::from_bits(0x1)); | |
| } | |
| #[test] | |
| fn check_f32() { | |
| // Constants | |
| assert_eq!(f32::EXP_SAT, 0b11111111); | |
| assert_eq!(f32::EXP_BIAS, 127); | |
| assert_eq!(f32::EXP_MAX, 127); | |
| assert_eq!(f32::EXP_MIN, -126); | |
| assert_eq!(f32::EXP_MIN_SUBNORM, -149); | |
| assert_biteq!(f32::NAN, <f32 as Float>::NAN); | |
| // Value of NAN and FLT_SNAN in C. We don't strictly need to match up, but it is good to | |
| // be aware if there are platforms where we don't. | |
| if MIPS_NAN { | |
| assert_biteq!(f32::NAN, f32::from_bits(0x7fbfffff)); | |
| assert_biteq!(f32::SNAN, f32::from_bits(0x7fffffff)); | |
| } else { | |
| assert_biteq!(f32::NAN, f32::from_bits(0x7fc00000)); | |
| assert_biteq!(f32::SNAN, f32::from_bits(0x7fa00000)); | |
| } | |
| assert!(f32::NAN.is_qnan()); | |
| // FIXME(rust-lang/rust#115567): x87 use in `is_snan` quiets the sNaN | |
| if !cfg!(x86_no_sse2) { | |
| assert!(f32::SNAN.is_snan()); | |
| } | |
| // `exp_unbiased` | |
| assert_eq!(f32::FRAC_PI_2.exp_unbiased(), 0); | |
| assert_eq!((1.0f32 / 2.0).exp_unbiased(), -1); | |
| assert_eq!(f32::MAX.exp_unbiased(), 127); | |
| assert_eq!(f32::MIN.exp_unbiased(), 127); | |
| assert_eq!(f32::MIN_POSITIVE.exp_unbiased(), -126); | |
| // This is a convenience method and not ldexp, `exp_unbiased` does not return correct | |
| // results for zero and subnormals. | |
| assert_eq!(f32::ZERO.exp_unbiased(), -127); | |
| assert_eq!(f32::from_bits(0x1).exp_unbiased(), -127); | |
| assert_eq!(f32::MIN_POSITIVE, f32::MIN_POSITIVE_NORMAL); | |
| // `from_parts` | |
| assert_biteq!(f32::from_parts(true, f32::EXP_BIAS, 0), -1.0f32); | |
| assert_biteq!( | |
| f32::from_parts(false, 10 + f32::EXP_BIAS, 0), | |
| hf32!("0x1p10") | |
| ); | |
| assert_biteq!(f32::from_parts(false, 0, 1), f32::from_bits(0x1)); | |
| } | |
| #[test] | |
| fn check_f64() { | |
| // Constants | |
| assert_eq!(f64::EXP_SAT, 0b11111111111); | |
| assert_eq!(f64::EXP_BIAS, 1023); | |
| assert_eq!(f64::EXP_MAX, 1023); | |
| assert_eq!(f64::EXP_MIN, -1022); | |
| assert_eq!(f64::EXP_MIN_SUBNORM, -1074); | |
| assert_biteq!(f64::NAN, <f64 as Float>::NAN); | |
| // Value of NAN and DBL_SNAN in C. We don't strictly need to match up, but it is good to | |
| // be aware if there are platforms where we don't. | |
| if MIPS_NAN { | |
| assert_biteq!(f64::NAN, f64::from_bits(0x7ff7ffffffffffff)); | |
| assert_biteq!(f64::SNAN, f64::from_bits(0x7fffffffffffffff)); | |
| } else { | |
| assert_biteq!(f64::NAN, f64::from_bits(0x7ff8000000000000)); | |
| assert_biteq!(f64::SNAN, f64::from_bits(0x7ff4000000000000)); | |
| } | |
| assert!(f64::NAN.is_qnan()); | |
| // FIXME(rust-lang/rust#115567): x87 use in `is_snan` quiets the sNaN | |
| if !cfg!(x86_no_sse2) { | |
| assert!(f64::SNAN.is_snan()); | |
| } | |
| // `exp_unbiased` | |
| assert_eq!(f64::FRAC_PI_2.exp_unbiased(), 0); | |
| assert_eq!((1.0f64 / 2.0).exp_unbiased(), -1); | |
| assert_eq!(f64::MAX.exp_unbiased(), 1023); | |
| assert_eq!(f64::MIN.exp_unbiased(), 1023); | |
| assert_eq!(f64::MIN_POSITIVE.exp_unbiased(), -1022); | |
| // This is a convenience method and not ldexp, `exp_unbiased` does not return correct | |
| // results for zero and subnormals. | |
| assert_eq!(f64::ZERO.exp_unbiased(), -1023); | |
| assert_eq!(f64::from_bits(0x1).exp_unbiased(), -1023); | |
| assert_eq!(f64::MIN_POSITIVE, f64::MIN_POSITIVE_NORMAL); | |
| // `from_parts` | |
| assert_biteq!(f64::from_parts(true, f64::EXP_BIAS, 0), -1.0f64); | |
| assert_biteq!( | |
| f64::from_parts(false, 10 + f64::EXP_BIAS, 0), | |
| hf64!("0x1p10") | |
| ); | |
| assert_biteq!(f64::from_parts(false, 0, 1), f64::from_bits(0x1)); | |
| } | |
| #[test] | |
| #[cfg(f128_enabled)] | |
| fn check_f128() { | |
| // Constants | |
| assert_eq!(f128::EXP_SAT, 0b111111111111111); | |
| assert_eq!(f128::EXP_BIAS, 16383); | |
| assert_eq!(f128::EXP_MAX, 16383); | |
| assert_eq!(f128::EXP_MIN, -16382); | |
| assert_eq!(f128::EXP_MIN_SUBNORM, -16494); | |
| assert_biteq!(f128::NAN, <f128 as Float>::NAN); | |
| // Value of NAN and FLT128_SNAN in C. We don't strictly need to match up, but it is good to | |
| // be aware if there are platforms where we don't. | |
| if MIPS_NAN { | |
| assert_biteq!( | |
| f128::NAN, | |
| f128::from_bits(0x7fff7fffffffffffffffffffffffffff) | |
| ); | |
| assert_biteq!( | |
| f128::SNAN, | |
| f128::from_bits(0x7fffffffffffffffffffffffffffffff) | |
| ); | |
| } else { | |
| assert_biteq!( | |
| f128::NAN, | |
| f128::from_bits(0x7fff8000000000000000000000000000) | |
| ); | |
| assert_biteq!( | |
| f128::SNAN, | |
| f128::from_bits(0x7fff4000000000000000000000000000) | |
| ); | |
| } | |
| assert!(f128::NAN.is_qnan()); | |
| assert!(f128::SNAN.is_snan()); | |
| // `exp_unbiased` | |
| assert_eq!(f128::FRAC_PI_2.exp_unbiased(), 0); | |
| assert_eq!((1.0f128 / 2.0).exp_unbiased(), -1); | |
| assert_eq!(f128::MAX.exp_unbiased(), 16383); | |
| assert_eq!(f128::MIN.exp_unbiased(), 16383); | |
| assert_eq!(f128::MIN_POSITIVE.exp_unbiased(), -16382); | |
| // This is a convenience method and not ldexp, `exp_unbiased` does not return correct | |
| // results for zero and subnormals. | |
| assert_eq!(f128::ZERO.exp_unbiased(), -16383); | |
| assert_eq!(f128::from_bits(0x1).exp_unbiased(), -16383); | |
| assert_eq!(f128::MIN_POSITIVE, f128::MIN_POSITIVE_NORMAL); | |
| // `from_parts` | |
| assert_biteq!(f128::from_parts(true, f128::EXP_BIAS, 0), -1.0f128); | |
| assert_biteq!(f128::from_parts(false, 0, 1), f128::from_bits(0x1)); | |
| } | |
| } |