From 15e9f560880a9873c399194003335be328f4d705 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 18 Jul 2023 15:22:56 -0400 Subject: [PATCH] Replace in-tree `rustc_apfloat` with the new version of the crate --- .reuse/dep5 | 5 - Cargo.lock | 8 +- compiler/rustc_apfloat/Cargo.toml | 8 - compiler/rustc_apfloat/src/ieee.rs | 2757 --------------------- compiler/rustc_apfloat/src/lib.rs | 695 ------ compiler/rustc_apfloat/src/ppc.rs | 434 ---- compiler/rustc_apfloat/tests/ieee.rs | 3301 -------------------------- compiler/rustc_apfloat/tests/ppc.rs | 530 ----- compiler/rustc_const_eval/Cargo.toml | 2 +- compiler/rustc_middle/Cargo.toml | 2 +- compiler/rustc_mir_build/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 2 + src/tools/tidy/src/style.rs | 4 - triagebot.toml | 9 - 14 files changed, 10 insertions(+), 7749 deletions(-) delete mode 100644 compiler/rustc_apfloat/Cargo.toml delete mode 100644 compiler/rustc_apfloat/src/ieee.rs delete mode 100644 compiler/rustc_apfloat/src/lib.rs delete mode 100644 compiler/rustc_apfloat/src/ppc.rs delete mode 100644 compiler/rustc_apfloat/tests/ieee.rs delete mode 100644 compiler/rustc_apfloat/tests/ppc.rs diff --git a/.reuse/dep5 b/.reuse/dep5 index 9a59f455fe9..5546a7cf391 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -38,11 +38,6 @@ Files: compiler/* Copyright: The Rust Project Developers (see https://thanks.rust-lang.org) License: MIT or Apache-2.0 -Files: compiler/rustc_apfloat/* -Copyright: LLVM APFloat authors - The Rust Project Developers (see https://thanks.rust-lang.org) -License: NCSA AND (MIT OR Apache-2.0) - Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs Copyright: The Cranelift Project Developers The Rust Project Developers (see https://thanks.rust-lang.org) diff --git a/Cargo.lock b/Cargo.lock index 72f0236fd08..cc5c163b8be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3135,7 +3135,9 @@ dependencies = [ [[package]] name = "rustc_apfloat" -version = "0.0.0" +version = "0.2.0+llvm-462a31f5a5ab" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be" dependencies = [ "bitflags 1.3.2", "smallvec", @@ -4725,9 +4727,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snap" diff --git a/compiler/rustc_apfloat/Cargo.toml b/compiler/rustc_apfloat/Cargo.toml deleted file mode 100644 index 98305201bc9..00000000000 --- a/compiler/rustc_apfloat/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "rustc_apfloat" -version = "0.0.0" -edition = "2021" - -[dependencies] -bitflags = "1.2.1" -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs deleted file mode 100644 index 2286712f025..00000000000 --- a/compiler/rustc_apfloat/src/ieee.rs +++ /dev/null @@ -1,2757 +0,0 @@ -use crate::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO}; -use crate::{Float, FloatConvert, ParseError, Round, Status, StatusAnd}; - -use core::cmp::{self, Ordering}; -use core::fmt::{self, Write}; -use core::marker::PhantomData; -use core::mem; -use core::ops::Neg; -use smallvec::{smallvec, SmallVec}; - -#[must_use] -pub struct IeeeFloat { - /// Absolute significand value (including the integer bit). - sig: [Limb; 1], - - /// The signed unbiased exponent of the value. - exp: ExpInt, - - /// What kind of floating point number this is. - category: Category, - - /// Sign bit of the number. - sign: bool, - - marker: PhantomData, -} - -/// Fundamental unit of big integer arithmetic, but also -/// large to store the largest significands by itself. -type Limb = u128; -const LIMB_BITS: usize = 128; -fn limbs_for_bits(bits: usize) -> usize { - (bits + LIMB_BITS - 1) / LIMB_BITS -} - -/// Enum that represents what fraction of the LSB truncated bits of an fp number -/// represent. -/// -/// This essentially combines the roles of guard and sticky bits. -#[must_use] -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum Loss { - // Example of truncated bits: - ExactlyZero, // 000000 - LessThanHalf, // 0xxxxx x's not all zero - ExactlyHalf, // 100000 - MoreThanHalf, // 1xxxxx x's not all zero -} - -/// Represents floating point arithmetic semantics. -pub trait Semantics: Sized { - /// Total number of bits in the in-memory format. - const BITS: usize; - - /// Number of bits in the significand. This includes the integer bit. - const PRECISION: usize; - - /// The largest E such that 2E is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2E is a normalized number; this - /// matches the definition of IEEE 754. - const MIN_EXP: ExpInt = -Self::MAX_EXP + 1; - - /// The significand bit that marks NaN as quiet. - const QNAN_BIT: usize = Self::PRECISION - 2; - - /// The significand bitpattern to mark a NaN as quiet. - /// NOTE: for X87DoubleExtended we need to set two bits instead of 2. - const QNAN_SIGNIFICAND: Limb = 1 << Self::QNAN_BIT; - - fn from_bits(bits: u128) -> IeeeFloat { - assert!(Self::BITS > Self::PRECISION); - - let sign = bits & (1 << (Self::BITS - 1)); - let exponent = (bits & !sign) >> (Self::PRECISION - 1); - let mut r = IeeeFloat { - sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)], - // Convert the exponent from its bias representation to a signed integer. - exp: (exponent as ExpInt) - Self::MAX_EXP, - category: Category::Zero, - sign: sign != 0, - marker: PhantomData, - }; - - if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Zero; - } else if r.exp == Self::MAX_EXP + 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Infinity; - } else if r.exp == Self::MAX_EXP + 1 && r.sig != [0] { - // Sign, exponent, significand meaningless. - r.category = Category::NaN; - } else { - r.category = Category::Normal; - if r.exp == Self::MIN_EXP - 1 { - // Denormal. - r.exp = Self::MIN_EXP; - } else { - // Set integer bit. - sig::set_bit(&mut r.sig, Self::PRECISION - 1); - } - } - - r - } - - fn to_bits(x: IeeeFloat) -> u128 { - assert!(Self::BITS > Self::PRECISION); - - // Split integer bit from significand. - let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1); - let mut significand = x.sig[0] & ((1 << (Self::PRECISION - 1)) - 1); - let exponent = match x.category { - Category::Normal => { - if x.exp == Self::MIN_EXP && !integer_bit { - // Denormal. - Self::MIN_EXP - 1 - } else { - x.exp - } - } - Category::Zero => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MIN_EXP - 1 - } - Category::Infinity => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MAX_EXP + 1 - } - Category::NaN => Self::MAX_EXP + 1, - }; - - // Convert the exponent from a signed integer to its bias representation. - let exponent = (exponent + Self::MAX_EXP) as u128; - - ((x.sign as u128) << (Self::BITS - 1)) | (exponent << (Self::PRECISION - 1)) | significand - } -} - -impl Copy for IeeeFloat {} -impl Clone for IeeeFloat { - fn clone(&self) -> Self { - *self - } -} - -macro_rules! ieee_semantics { - ($($name:ident = $sem:ident($bits:tt : $exp_bits:tt)),*) => { - $(pub struct $sem;)* - $(pub type $name = IeeeFloat<$sem>;)* - $(impl Semantics for $sem { - const BITS: usize = $bits; - const PRECISION: usize = ($bits - 1 - $exp_bits) + 1; - const MAX_EXP: ExpInt = (1 << ($exp_bits - 1)) - 1; - })* - } -} - -ieee_semantics! { - Half = HalfS(16:5), - Single = SingleS(32:8), - Double = DoubleS(64:11), - Quad = QuadS(128:15) -} - -pub struct X87DoubleExtendedS; -pub type X87DoubleExtended = IeeeFloat; -impl Semantics for X87DoubleExtendedS { - const BITS: usize = 80; - const PRECISION: usize = 64; - const MAX_EXP: ExpInt = (1 << (15 - 1)) - 1; - - /// For x87 extended precision, we want to make a NaN, not a - /// pseudo-NaN. Maybe we should expose the ability to make - /// pseudo-NaNs? - const QNAN_SIGNIFICAND: Limb = 0b11 << Self::QNAN_BIT; - - /// Integer bit is explicit in this format. Intel hardware (387 and later) - /// does not support these bit patterns: - /// exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity") - /// exponent = all 1's, integer bit 0, significand nonzero ("pseudoNaN") - /// exponent = 0, integer bit 1 ("pseudodenormal") - /// exponent != 0 nor all 1's, integer bit 0 ("unnormal") - /// At the moment, the first two are treated as NaNs, the second two as Normal. - fn from_bits(bits: u128) -> IeeeFloat { - let sign = bits & (1 << (Self::BITS - 1)); - let exponent = (bits & !sign) >> Self::PRECISION; - let mut r = IeeeFloat { - sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)], - // Convert the exponent from its bias representation to a signed integer. - exp: (exponent as ExpInt) - Self::MAX_EXP, - category: Category::Zero, - sign: sign != 0, - marker: PhantomData, - }; - - if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Zero; - } else if r.exp == Self::MAX_EXP + 1 && r.sig == [1 << (Self::PRECISION - 1)] { - // Exponent, significand meaningless. - r.category = Category::Infinity; - } else if r.exp == Self::MAX_EXP + 1 && r.sig != [1 << (Self::PRECISION - 1)] { - // Sign, exponent, significand meaningless. - r.category = Category::NaN; - } else { - r.category = Category::Normal; - if r.exp == Self::MIN_EXP - 1 { - // Denormal. - r.exp = Self::MIN_EXP; - } - } - - r - } - - fn to_bits(x: IeeeFloat) -> u128 { - // Get integer bit from significand. - let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1); - let mut significand = x.sig[0] & ((1 << Self::PRECISION) - 1); - let exponent = match x.category { - Category::Normal => { - if x.exp == Self::MIN_EXP && !integer_bit { - // Denormal. - Self::MIN_EXP - 1 - } else { - x.exp - } - } - Category::Zero => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MIN_EXP - 1 - } - Category::Infinity => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 1 << (Self::PRECISION - 1); - Self::MAX_EXP + 1 - } - Category::NaN => Self::MAX_EXP + 1, - }; - - // Convert the exponent from a signed integer to its bias representation. - let exponent = (exponent + Self::MAX_EXP) as u128; - - ((x.sign as u128) << (Self::BITS - 1)) | (exponent << Self::PRECISION) | significand - } -} - -float_common_impls!(IeeeFloat); - -impl PartialEq for IeeeFloat { - fn eq(&self, rhs: &Self) -> bool { - self.partial_cmp(rhs) == Some(Ordering::Equal) - } -} - -impl PartialOrd for IeeeFloat { - fn partial_cmp(&self, rhs: &Self) -> Option { - match (self.category, rhs.category) { - (Category::NaN, _) | (_, Category::NaN) => None, - - (Category::Infinity, Category::Infinity) => Some((!self.sign).cmp(&(!rhs.sign))), - - (Category::Zero, Category::Zero) => Some(Ordering::Equal), - - (Category::Infinity, _) | (Category::Normal, Category::Zero) => { - Some((!self.sign).cmp(&self.sign)) - } - - (_, Category::Infinity) | (Category::Zero, Category::Normal) => { - Some(rhs.sign.cmp(&(!rhs.sign))) - } - - (Category::Normal, Category::Normal) => { - // Two normal numbers. Do they have the same sign? - Some((!self.sign).cmp(&(!rhs.sign)).then_with(|| { - // Compare absolute values; invert result if negative. - let result = self.cmp_abs_normal(*rhs); - - if self.sign { result.reverse() } else { result } - })) - } - } - } -} - -impl Neg for IeeeFloat { - type Output = Self; - fn neg(mut self) -> Self { - self.sign = !self.sign; - self - } -} - -/// Prints this value as a decimal string. -/// -/// \param precision The maximum number of digits of -/// precision to output. If there are fewer digits available, -/// zero padding will not be used unless the value is -/// integral and small enough to be expressed in -/// precision digits. 0 means to use the natural -/// precision of the number. -/// \param width The maximum number of zeros to -/// consider inserting before falling back to scientific -/// notation. 0 means to always use scientific notation. -/// -/// \param alternate Indicate whether to remove the trailing zero in -/// fraction part or not. Also setting this parameter to true forces -/// producing of output more similar to default printf behavior. -/// Specifically the lower e is used as exponent delimiter and exponent -/// always contains no less than two digits. -/// -/// Number precision width Result -/// ------ --------- ----- ------ -/// 1.01E+4 5 2 10100 -/// 1.01E+4 4 2 1.01E+4 -/// 1.01E+4 5 1 1.01E+4 -/// 1.01E-2 5 2 0.0101 -/// 1.01E-2 4 2 0.0101 -/// 1.01E-2 4 1 1.01E-2 -impl fmt::Display for IeeeFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let width = f.width().unwrap_or(3); - let alternate = f.alternate(); - - match self.category { - Category::Infinity => { - if self.sign { - return f.write_str("-Inf"); - } else { - return f.write_str("+Inf"); - } - } - - Category::NaN => return f.write_str("NaN"), - - Category::Zero => { - if self.sign { - f.write_char('-')?; - } - - if width == 0 { - if alternate { - f.write_str("0.0")?; - if let Some(n) = f.precision() { - for _ in 1..n { - f.write_char('0')?; - } - } - f.write_str("e+00")?; - } else { - f.write_str("0.0E+0")?; - } - } else { - f.write_char('0')?; - } - return Ok(()); - } - - Category::Normal => {} - } - - if self.sign { - f.write_char('-')?; - } - - // We use enough digits so the number can be round-tripped back to an - // APFloat. The formula comes from "How to Print Floating-Point Numbers - // Accurately" by Steele and White. - // FIXME: Using a formula based purely on the precision is conservative; - // we can print fewer digits depending on the actual value being printed. - - // precision = 2 + floor(S::PRECISION / lg_2(10)) - let precision = f.precision().unwrap_or(2 + S::PRECISION * 59 / 196); - - // Decompose the number into an APInt and an exponent. - let mut exp = self.exp - (S::PRECISION as ExpInt - 1); - let mut sig = vec![self.sig[0]]; - - // Ignore trailing binary zeros. - let trailing_zeros = sig[0].trailing_zeros(); - let _: Loss = sig::shift_right(&mut sig, &mut exp, trailing_zeros as usize); - - // Change the exponent from 2^e to 10^e. - if exp == 0 { - // Nothing to do. - } else if exp > 0 { - // Just shift left. - let shift = exp as usize; - sig.resize(limbs_for_bits(S::PRECISION + shift), 0); - sig::shift_left(&mut sig, &mut exp, shift); - } else { - // exp < 0 - let mut texp = -exp as usize; - - // We transform this using the identity: - // (N)(2^-e) == (N)(5^e)(10^-e) - - // Multiply significand by 5^e. - // N * 5^0101 == N * 5^(1*1) * 5^(0*2) * 5^(1*4) * 5^(0*8) - let mut sig_scratch = vec![]; - let mut p5 = vec![]; - let mut p5_scratch = vec![]; - while texp != 0 { - if p5.is_empty() { - p5.push(5); - } else { - p5_scratch.resize(p5.len() * 2, 0); - let _: Loss = - sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS); - while p5_scratch.last() == Some(&0) { - p5_scratch.pop(); - } - mem::swap(&mut p5, &mut p5_scratch); - } - if texp & 1 != 0 { - sig_scratch.resize(sig.len() + p5.len(), 0); - let _: Loss = sig::mul( - &mut sig_scratch, - &mut 0, - &sig, - &p5, - (sig.len() + p5.len()) * LIMB_BITS, - ); - while sig_scratch.last() == Some(&0) { - sig_scratch.pop(); - } - mem::swap(&mut sig, &mut sig_scratch); - } - texp >>= 1; - } - } - - // Fill the buffer. - let mut buffer = vec![]; - - // Ignore digits from the significand until it is no more - // precise than is required for the desired precision. - // 196/59 is a very slight overestimate of lg_2(10). - let required = (precision * 196 + 58) / 59; - let mut discard_digits = sig::omsb(&sig).saturating_sub(required) * 59 / 196; - let mut in_trail = true; - while !sig.is_empty() { - // Perform short division by 10 to extract the rightmost digit. - // rem <- sig % 10 - // sig <- sig / 10 - let mut rem = 0; - - // Use 64-bit division and remainder, with 32-bit chunks from sig. - sig::each_chunk(&mut sig, 32, |chunk| { - let chunk = chunk as u32; - let combined = ((rem as u64) << 32) | (chunk as u64); - rem = (combined % 10) as u8; - (combined / 10) as u32 as Limb - }); - - // Reduce the significand to avoid wasting time dividing 0's. - while sig.last() == Some(&0) { - sig.pop(); - } - - let digit = rem; - - // Ignore digits we don't need. - if discard_digits > 0 { - discard_digits -= 1; - exp += 1; - continue; - } - - // Drop trailing zeros. - if in_trail && digit == 0 { - exp += 1; - } else { - in_trail = false; - buffer.push(b'0' + digit); - } - } - - assert!(!buffer.is_empty(), "no characters in buffer!"); - - // Drop down to precision. - // FIXME: don't do more precise calculations above than are required. - if buffer.len() > precision { - // The most significant figures are the last ones in the buffer. - let mut first_sig = buffer.len() - precision; - - // Round. - // FIXME: this probably shouldn't use 'round half up'. - - // Rounding down is just a truncation, except we also want to drop - // trailing zeros from the new result. - if buffer[first_sig - 1] < b'5' { - while first_sig < buffer.len() && buffer[first_sig] == b'0' { - first_sig += 1; - } - } else { - // Rounding up requires a decimal add-with-carry. If we continue - // the carry, the newly-introduced zeros will just be truncated. - for x in &mut buffer[first_sig..] { - if *x == b'9' { - first_sig += 1; - } else { - *x += 1; - break; - } - } - } - - exp += first_sig as ExpInt; - buffer.drain(..first_sig); - - // If we carried through, we have exactly one digit of precision. - if buffer.is_empty() { - buffer.push(b'1'); - } - } - - let digits = buffer.len(); - - // Check whether we should use scientific notation. - let scientific = if width == 0 { - true - } else if exp >= 0 { - // 765e3 --> 765000 - // ^^^ - // But we shouldn't make the number look more precise than it is. - exp as usize > width || digits + exp as usize > precision - } else { - // Power of the most significant digit. - let msd = exp + (digits - 1) as ExpInt; - if msd >= 0 { - // 765e-2 == 7.65 - false - } else { - // 765e-5 == 0.00765 - // ^ ^^ - -msd as usize > width - } - }; - - // Scientific formatting is pretty straightforward. - if scientific { - exp += digits as ExpInt - 1; - - f.write_char(buffer[digits - 1] as char)?; - f.write_char('.')?; - let truncate_zero = !alternate; - if digits == 1 && truncate_zero { - f.write_char('0')?; - } else { - for &d in buffer[..digits - 1].iter().rev() { - f.write_char(d as char)?; - } - } - // Fill with zeros up to precision. - if !truncate_zero && precision > digits - 1 { - for _ in 0..=precision - digits { - f.write_char('0')?; - } - } - // For alternate we use lower 'e'. - f.write_char(if alternate { 'e' } else { 'E' })?; - - // Exponent always at least two digits if we do not truncate zeros. - if truncate_zero { - write!(f, "{:+}", exp)?; - } else { - write!(f, "{:+03}", exp)?; - } - - return Ok(()); - } - - // Non-scientific, positive exponents. - if exp >= 0 { - for &d in buffer.iter().rev() { - f.write_char(d as char)?; - } - for _ in 0..exp { - f.write_char('0')?; - } - return Ok(()); - } - - // Non-scientific, negative exponents. - let unit_place = -exp as usize; - if unit_place < digits { - for &d in buffer[unit_place..].iter().rev() { - f.write_char(d as char)?; - } - f.write_char('.')?; - for &d in buffer[..unit_place].iter().rev() { - f.write_char(d as char)?; - } - } else { - f.write_str("0.")?; - for _ in digits..unit_place { - f.write_char('0')?; - } - for &d in buffer.iter().rev() { - f.write_char(d as char)?; - } - } - - Ok(()) - } -} - -impl fmt::Debug for IeeeFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}({:?} | {}{:?} * 2^{})", - self, - self.category, - if self.sign { "-" } else { "+" }, - self.sig, - self.exp - ) - } -} - -impl Float for IeeeFloat { - const BITS: usize = S::BITS; - const PRECISION: usize = S::PRECISION; - const MAX_EXP: ExpInt = S::MAX_EXP; - const MIN_EXP: ExpInt = S::MIN_EXP; - - const ZERO: Self = IeeeFloat { - sig: [0], - exp: S::MIN_EXP - 1, - category: Category::Zero, - sign: false, - marker: PhantomData, - }; - - const INFINITY: Self = IeeeFloat { - sig: [0], - exp: S::MAX_EXP + 1, - category: Category::Infinity, - sign: false, - marker: PhantomData, - }; - - // FIXME(eddyb) remove when qnan becomes const fn. - const NAN: Self = IeeeFloat { - sig: [S::QNAN_SIGNIFICAND], - exp: S::MAX_EXP + 1, - category: Category::NaN, - sign: false, - marker: PhantomData, - }; - - fn qnan(payload: Option) -> Self { - IeeeFloat { - sig: [S::QNAN_SIGNIFICAND - | payload.map_or(0, |payload| { - // Zero out the excess bits of the significand. - payload & ((1 << S::QNAN_BIT) - 1) - })], - exp: S::MAX_EXP + 1, - category: Category::NaN, - sign: false, - marker: PhantomData, - } - } - - fn snan(payload: Option) -> Self { - let mut snan = Self::qnan(payload); - - // We always have to clear the QNaN bit to make it an SNaN. - sig::clear_bit(&mut snan.sig, S::QNAN_BIT); - - // If there are no bits set in the payload, we have to set - // *something* to make it a NaN instead of an infinity; - // conventionally, this is the next bit down from the QNaN bit. - if snan.sig[0] & !S::QNAN_SIGNIFICAND == 0 { - sig::set_bit(&mut snan.sig, S::QNAN_BIT - 1); - } - - snan - } - - fn largest() -> Self { - // We want (in interchange format): - // exponent = 1..10 - // significand = 1..1 - IeeeFloat { - sig: [(1 << S::PRECISION) - 1], - exp: S::MAX_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - } - - // We want (in interchange format): - // exponent = 0..0 - // significand = 0..01 - const SMALLEST: Self = IeeeFloat { - sig: [1], - exp: S::MIN_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - - fn smallest_normalized() -> Self { - // We want (in interchange format): - // exponent = 0..0 - // significand = 10..0 - IeeeFloat { - sig: [1 << (S::PRECISION - 1)], - exp: S::MIN_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - } - - fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd { - let status = match (self.category, rhs.category) { - (Category::Infinity, Category::Infinity) => { - // Differently signed infinities can only be validly - // subtracted. - if self.sign != rhs.sign { - self = Self::NAN; - Status::INVALID_OP - } else { - Status::OK - } - } - - // Sign may depend on rounding mode; handled below. - (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => { - Status::OK - } - - (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => { - self = rhs; - Status::OK - } - - // This return code means it was not a simple case. - (Category::Normal, Category::Normal) => { - let loss = sig::add_or_sub( - &mut self.sig, - &mut self.exp, - &mut self.sign, - &mut [rhs.sig[0]], - rhs.exp, - rhs.sign, - ); - let status; - self = unpack!(status=, self.normalize(round, loss)); - - // Can only be zero if we lost no fraction. - assert!(self.category != Category::Zero || loss == Loss::ExactlyZero); - - status - } - }; - - // If two numbers add (exactly) to zero, IEEE 754 decrees it is a - // positive zero unless rounding to minus infinity, except that - // adding two like-signed zeroes gives that zero. - if self.category == Category::Zero - && (rhs.category != Category::Zero || self.sign != rhs.sign) - { - self.sign = round == Round::TowardNegative; - } - - status.and(self) - } - - fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd { - self.sign ^= rhs.sign; - - match (self.category, rhs.category) { - (Category::NaN, _) => { - self.sign = false; - Status::OK.and(self) - } - - (_, Category::NaN) => { - self.sign = false; - self.category = Category::NaN; - self.sig = rhs.sig; - Status::OK.and(self) - } - - (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => { - Status::INVALID_OP.and(Self::NAN) - } - - (_, Category::Infinity) | (Category::Infinity, _) => { - self.category = Category::Infinity; - Status::OK.and(self) - } - - (Category::Zero, _) | (_, Category::Zero) => { - self.category = Category::Zero; - Status::OK.and(self) - } - - (Category::Normal, Category::Normal) => { - self.exp += rhs.exp; - let mut wide_sig = [0; 2]; - let loss = - sig::mul(&mut wide_sig, &mut self.exp, &self.sig, &rhs.sig, S::PRECISION); - self.sig = [wide_sig[0]]; - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - status.and(self) - } - } - } - - fn mul_add_r(mut self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd { - // If and only if all arguments are normal do we need to do an - // extended-precision calculation. - if !self.is_finite_non_zero() || !multiplicand.is_finite_non_zero() || !addend.is_finite() { - let mut status; - self = unpack!(status=, self.mul_r(multiplicand, round)); - - // FS can only be Status::OK or Status::INVALID_OP. There is no more work - // to do in the latter case. The IEEE-754R standard says it is - // implementation-defined in this case whether, if ADDEND is a - // quiet NaN, we raise invalid op; this implementation does so. - // - // If we need to do the addition we can do so with normal - // precision. - if status == Status::OK { - self = unpack!(status=, self.add_r(addend, round)); - } - return status.and(self); - } - - // Post-multiplication sign, before addition. - self.sign ^= multiplicand.sign; - - // Allocate space for twice as many bits as the original significand, plus one - // extra bit for the addition to overflow into. - assert!(limbs_for_bits(S::PRECISION * 2 + 1) <= 2); - let mut wide_sig = sig::widening_mul(self.sig[0], multiplicand.sig[0]); - - let mut loss = Loss::ExactlyZero; - let mut omsb = sig::omsb(&wide_sig); - self.exp += multiplicand.exp; - - // Assume the operands involved in the multiplication are single-precision - // FP, and the two multiplicants are: - // lhs = a23 . a22 ... a0 * 2^e1 - // rhs = b23 . b22 ... b0 * 2^e2 - // the result of multiplication is: - // lhs = c48 c47 c46 . c45 ... c0 * 2^(e1+e2) - // Note that there are three significant bits at the left-hand side of the - // radix point: two for the multiplication, and an overflow bit for the - // addition (that will always be zero at this point). Move the radix point - // toward left by two bits, and adjust exponent accordingly. - self.exp += 2; - - if addend.is_non_zero() { - // Normalize our MSB to one below the top bit to allow for overflow. - let ext_precision = 2 * S::PRECISION + 1; - if omsb != ext_precision - 1 { - assert!(ext_precision > omsb); - sig::shift_left(&mut wide_sig, &mut self.exp, (ext_precision - 1) - omsb); - } - - // The intermediate result of the multiplication has "2 * S::PRECISION" - // significant bit; adjust the addend to be consistent with mul result. - let mut ext_addend_sig = [addend.sig[0], 0]; - - // Extend the addend significand to ext_precision - 1. This guarantees - // that the high bit of the significand is zero (same as wide_sig), - // so the addition will overflow (if it does overflow at all) into the top bit. - sig::shift_left(&mut ext_addend_sig, &mut 0, ext_precision - 1 - S::PRECISION); - loss = sig::add_or_sub( - &mut wide_sig, - &mut self.exp, - &mut self.sign, - &mut ext_addend_sig, - addend.exp + 1, - addend.sign, - ); - - omsb = sig::omsb(&wide_sig); - } - - // Convert the result having "2 * S::PRECISION" significant-bits back to the one - // having "S::PRECISION" significant-bits. First, move the radix point from - // position "2*S::PRECISION - 1" to "S::PRECISION - 1". The exponent need to be - // adjusted by "2*S::PRECISION - 1" - "S::PRECISION - 1" = "S::PRECISION". - self.exp -= S::PRECISION as ExpInt + 1; - - // In case MSB resides at the left-hand side of radix point, shift the - // mantissa right by some amount to make sure the MSB reside right before - // the radix point (i.e., "MSB . rest-significant-bits"). - if omsb > S::PRECISION { - let bits = omsb - S::PRECISION; - loss = sig::shift_right(&mut wide_sig, &mut self.exp, bits).combine(loss); - } - - self.sig[0] = wide_sig[0]; - - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - - // If two numbers add (exactly) to zero, IEEE 754 decrees it is a - // positive zero unless rounding to minus infinity, except that - // adding two like-signed zeroes gives that zero. - if self.category == Category::Zero - && !status.intersects(Status::UNDERFLOW) - && self.sign != addend.sign - { - self.sign = round == Round::TowardNegative; - } - - status.and(self) - } - - fn div_r(mut self, rhs: Self, round: Round) -> StatusAnd { - self.sign ^= rhs.sign; - - match (self.category, rhs.category) { - (Category::NaN, _) => { - self.sign = false; - Status::OK.and(self) - } - - (_, Category::NaN) => { - self.category = Category::NaN; - self.sig = rhs.sig; - self.sign = false; - Status::OK.and(self) - } - - (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => { - Status::INVALID_OP.and(Self::NAN) - } - - (Category::Infinity | Category::Zero, _) => Status::OK.and(self), - - (Category::Normal, Category::Infinity) => { - self.category = Category::Zero; - Status::OK.and(self) - } - - (Category::Normal, Category::Zero) => { - self.category = Category::Infinity; - Status::DIV_BY_ZERO.and(self) - } - - (Category::Normal, Category::Normal) => { - self.exp -= rhs.exp; - let dividend = self.sig[0]; - let loss = sig::div( - &mut self.sig, - &mut self.exp, - &mut [dividend], - &mut [rhs.sig[0]], - S::PRECISION, - ); - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - status.and(self) - } - } - } - - fn c_fmod(mut self, rhs: Self) -> StatusAnd { - match (self.category, rhs.category) { - (Category::NaN, _) - | (Category::Zero, Category::Infinity | Category::Normal) - | (Category::Normal, Category::Infinity) => Status::OK.and(self), - - (_, Category::NaN) => { - self.sign = false; - self.category = Category::NaN; - self.sig = rhs.sig; - Status::OK.and(self) - } - - (Category::Infinity, _) | (_, Category::Zero) => Status::INVALID_OP.and(Self::NAN), - - (Category::Normal, Category::Normal) => { - while self.is_finite_non_zero() - && rhs.is_finite_non_zero() - && self.cmp_abs_normal(rhs) != Ordering::Less - { - let mut v = rhs.scalbn(self.ilogb() - rhs.ilogb()); - if self.cmp_abs_normal(v) == Ordering::Less { - v = v.scalbn(-1); - } - v.sign = self.sign; - - let status; - self = unpack!(status=, self - v); - assert_eq!(status, Status::OK); - } - Status::OK.and(self) - } - } - } - - fn round_to_integral(self, round: Round) -> StatusAnd { - // If the exponent is large enough, we know that this value is already - // integral, and the arithmetic below would potentially cause it to saturate - // to +/-Inf. Bail out early instead. - if self.is_finite_non_zero() && self.exp + 1 >= S::PRECISION as ExpInt { - return Status::OK.and(self); - } - - // The algorithm here is quite simple: we add 2^(p-1), where p is the - // precision of our format, and then subtract it back off again. The choice - // of rounding modes for the addition/subtraction determines the rounding mode - // for our integral rounding as well. - // NOTE: When the input value is negative, we do subtraction followed by - // addition instead. - assert!(S::PRECISION <= 128); - let mut status; - let magic_const = unpack!(status=, Self::from_u128(1 << (S::PRECISION - 1))); - let magic_const = magic_const.copy_sign(self); - - if status != Status::OK { - return status.and(self); - } - - let mut r = self; - r = unpack!(status=, r.add_r(magic_const, round)); - if status != Status::OK && status != Status::INEXACT { - return status.and(self); - } - - // Restore the input sign to handle 0.0/-0.0 cases correctly. - r.sub_r(magic_const, round).map(|r| r.copy_sign(self)) - } - - fn next_up(mut self) -> StatusAnd { - // Compute nextUp(x), handling each float category separately. - match self.category { - Category::Infinity => { - if self.sign { - // nextUp(-inf) = -largest - Status::OK.and(-Self::largest()) - } else { - // nextUp(+inf) = +inf - Status::OK.and(self) - } - } - Category::NaN => { - // IEEE-754R 2008 6.2 Par 2: nextUp(sNaN) = qNaN. Set Invalid flag. - // IEEE-754R 2008 6.2: nextUp(qNaN) = qNaN. Must be identity so we do not - // change the payload. - if self.is_signaling() { - // For consistency, propagate the sign of the sNaN to the qNaN. - Status::INVALID_OP.and(Self::NAN.copy_sign(self)) - } else { - Status::OK.and(self) - } - } - Category::Zero => { - // nextUp(pm 0) = +smallest - Status::OK.and(Self::SMALLEST) - } - Category::Normal => { - // nextUp(-smallest) = -0 - if self.is_smallest() && self.sign { - return Status::OK.and(-Self::ZERO); - } - - // nextUp(largest) == INFINITY - if self.is_largest() && !self.sign { - return Status::OK.and(Self::INFINITY); - } - - // Excluding the integral bit. This allows us to test for binade boundaries. - let sig_mask = (1 << (S::PRECISION - 1)) - 1; - - // nextUp(normal) == normal + inc. - if self.sign { - // If we are negative, we need to decrement the significand. - - // We only cross a binade boundary that requires adjusting the exponent - // if: - // 1. exponent != S::MIN_EXP. This implies we are not in the - // smallest binade or are dealing with denormals. - // 2. Our significand excluding the integral bit is all zeros. - let crossing_binade_boundary = - self.exp != S::MIN_EXP && self.sig[0] & sig_mask == 0; - - // Decrement the significand. - // - // We always do this since: - // 1. If we are dealing with a non-binade decrement, by definition we - // just decrement the significand. - // 2. If we are dealing with a normal -> normal binade decrement, since - // we have an explicit integral bit the fact that all bits but the - // integral bit are zero implies that subtracting one will yield a - // significand with 0 integral bit and 1 in all other spots. Thus we - // must just adjust the exponent and set the integral bit to 1. - // 3. If we are dealing with a normal -> denormal binade decrement, - // since we set the integral bit to 0 when we represent denormals, we - // just decrement the significand. - sig::decrement(&mut self.sig); - - if crossing_binade_boundary { - // Our result is a normal number. Do the following: - // 1. Set the integral bit to 1. - // 2. Decrement the exponent. - sig::set_bit(&mut self.sig, S::PRECISION - 1); - self.exp -= 1; - } - } else { - // If we are positive, we need to increment the significand. - - // We only cross a binade boundary that requires adjusting the exponent if - // the input is not a denormal and all of said input's significand bits - // are set. If all of said conditions are true: clear the significand, set - // the integral bit to 1, and increment the exponent. If we have a - // denormal always increment since moving denormals and the numbers in the - // smallest normal binade have the same exponent in our representation. - let crossing_binade_boundary = - !self.is_denormal() && self.sig[0] & sig_mask == sig_mask; - - if crossing_binade_boundary { - self.sig = [0]; - sig::set_bit(&mut self.sig, S::PRECISION - 1); - assert_ne!( - self.exp, - S::MAX_EXP, - "We can not increment an exponent beyond the MAX_EXP \ - allowed by the given floating point semantics." - ); - self.exp += 1; - } else { - sig::increment(&mut self.sig); - } - } - Status::OK.and(self) - } - } - } - - fn from_bits(input: u128) -> Self { - // Dispatch to semantics. - S::from_bits(input) - } - - fn from_u128_r(input: u128, round: Round) -> StatusAnd { - IeeeFloat { - sig: [input], - exp: S::PRECISION as ExpInt - 1, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - .normalize(round, Loss::ExactlyZero) - } - - fn from_str_r(mut s: &str, mut round: Round) -> Result, ParseError> { - if s.is_empty() { - return Err(ParseError("Invalid string length")); - } - - // Handle special cases. - match s { - "inf" | "INFINITY" => return Ok(Status::OK.and(Self::INFINITY)), - "-inf" | "-INFINITY" => return Ok(Status::OK.and(-Self::INFINITY)), - "nan" | "NaN" => return Ok(Status::OK.and(Self::NAN)), - "-nan" | "-NaN" => return Ok(Status::OK.and(-Self::NAN)), - _ => {} - } - - // Handle a leading minus sign. - let minus = s.starts_with('-'); - if minus || s.starts_with('+') { - s = &s[1..]; - if s.is_empty() { - return Err(ParseError("String has no digits")); - } - } - - // Adjust the rounding mode for the absolute value below. - if minus { - round = -round; - } - - let r = if s.starts_with("0x") || s.starts_with("0X") { - s = &s[2..]; - if s.is_empty() { - return Err(ParseError("Invalid string")); - } - Self::from_hexadecimal_string(s, round)? - } else { - Self::from_decimal_string(s, round)? - }; - - Ok(r.map(|r| if minus { -r } else { r })) - } - - fn to_bits(self) -> u128 { - // Dispatch to semantics. - S::to_bits(self) - } - - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - // The result of trying to convert a number too large. - let overflow = if self.sign { - // Negative numbers cannot be represented as unsigned. - 0 - } else { - // Largest unsigned integer of the given width. - !0 >> (128 - width) - }; - - *is_exact = false; - - match self.category { - Category::NaN => Status::INVALID_OP.and(0), - - Category::Infinity => Status::INVALID_OP.and(overflow), - - Category::Zero => { - // Negative zero can't be represented as an int. - *is_exact = !self.sign; - Status::OK.and(0) - } - - Category::Normal => { - let mut r = 0; - - // Step 1: place our absolute value, with any fraction truncated, in - // the destination. - let truncated_bits = if self.exp < 0 { - // Our absolute value is less than one; truncate everything. - // For exponent -1 the integer bit represents .5, look at that. - // For smaller exponents leftmost truncated bit is 0. - S::PRECISION - 1 + (-self.exp) as usize - } else { - // We want the most significant (exponent + 1) bits; the rest are - // truncated. - let bits = self.exp as usize + 1; - - // Hopelessly large in magnitude? - if bits > width { - return Status::INVALID_OP.and(overflow); - } - - if bits < S::PRECISION { - // We truncate (S::PRECISION - bits) bits. - r = self.sig[0] >> (S::PRECISION - bits); - S::PRECISION - bits - } else { - // We want at least as many bits as are available. - r = self.sig[0] << (bits - S::PRECISION); - 0 - } - }; - - // Step 2: work out any lost fraction, and increment the absolute - // value if we would round away from zero. - let mut loss = Loss::ExactlyZero; - if truncated_bits > 0 { - loss = Loss::through_truncation(&self.sig, truncated_bits); - if loss != Loss::ExactlyZero - && self.round_away_from_zero(round, loss, truncated_bits) - { - r = r.wrapping_add(1); - if r == 0 { - return Status::INVALID_OP.and(overflow); // Overflow. - } - } - } - - // Step 3: check if we fit in the destination. - if r > overflow { - return Status::INVALID_OP.and(overflow); - } - - if loss == Loss::ExactlyZero { - *is_exact = true; - Status::OK.and(r) - } else { - Status::INEXACT.and(r) - } - } - } - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering { - assert!(self.is_finite_non_zero()); - assert!(rhs.is_finite_non_zero()); - - // If exponents are equal, do an unsigned comparison of the significands. - self.exp.cmp(&rhs.exp).then_with(|| sig::cmp(&self.sig, &rhs.sig)) - } - - fn bitwise_eq(self, rhs: Self) -> bool { - if self.category != rhs.category || self.sign != rhs.sign { - return false; - } - - if self.category == Category::Zero || self.category == Category::Infinity { - return true; - } - - if self.is_finite_non_zero() && self.exp != rhs.exp { - return false; - } - - self.sig == rhs.sig - } - - fn is_negative(self) -> bool { - self.sign - } - - fn is_denormal(self) -> bool { - self.is_finite_non_zero() - && self.exp == S::MIN_EXP - && !sig::get_bit(&self.sig, S::PRECISION - 1) - } - - fn is_signaling(self) -> bool { - // IEEE-754R 2008 6.2.1: A signaling NaN bit string should be encoded with the - // first bit of the trailing significand being 0. - self.is_nan() && !sig::get_bit(&self.sig, S::QNAN_BIT) - } - - fn category(self) -> Category { - self.category - } - - fn get_exact_inverse(self) -> Option { - // Special floats and denormals have no exact inverse. - if !self.is_finite_non_zero() { - return None; - } - - // Check that the number is a power of two by making sure that only the - // integer bit is set in the significand. - if self.sig != [1 << (S::PRECISION - 1)] { - return None; - } - - // Get the inverse. - let mut reciprocal = Self::from_u128(1).value; - let status; - reciprocal = unpack!(status=, reciprocal / self); - if status != Status::OK { - return None; - } - - // Avoid multiplication with a denormal, it is not safe on all platforms and - // may be slower than a normal division. - if reciprocal.is_denormal() { - return None; - } - - assert!(reciprocal.is_finite_non_zero()); - assert_eq!(reciprocal.sig, [1 << (S::PRECISION - 1)]); - - Some(reciprocal) - } - - fn ilogb(mut self) -> ExpInt { - if self.is_nan() { - return IEK_NAN; - } - if self.is_zero() { - return IEK_ZERO; - } - if self.is_infinite() { - return IEK_INF; - } - if !self.is_denormal() { - return self.exp; - } - - let sig_bits = (S::PRECISION - 1) as ExpInt; - self.exp += sig_bits; - self = self.normalize(Round::NearestTiesToEven, Loss::ExactlyZero).value; - self.exp - sig_bits - } - - fn scalbn_r(mut self, exp: ExpInt, round: Round) -> Self { - // If exp is wildly out-of-scale, simply adding it to self.exp will - // overflow; clamp it to a safe range before adding, but ensure that the range - // is large enough that the clamp does not change the result. The range we - // need to support is the difference between the largest possible exponent and - // the normalized exponent of half the smallest denormal. - - let sig_bits = (S::PRECISION - 1) as i32; - let max_change = S::MAX_EXP as i32 - (S::MIN_EXP as i32 - sig_bits) + 1; - - // Clamp to one past the range ends to let normalize handle overflow. - let exp_change = cmp::min(cmp::max(exp as i32, -max_change - 1), max_change); - self.exp = self.exp.saturating_add(exp_change as ExpInt); - self = self.normalize(round, Loss::ExactlyZero).value; - if self.is_nan() { - sig::set_bit(&mut self.sig, S::QNAN_BIT); - } - self - } - - fn frexp_r(mut self, exp: &mut ExpInt, round: Round) -> Self { - *exp = self.ilogb(); - - // Quiet signalling nans. - if *exp == IEK_NAN { - sig::set_bit(&mut self.sig, S::QNAN_BIT); - return self; - } - - if *exp == IEK_INF { - return self; - } - - // 1 is added because frexp is defined to return a normalized fraction in - // +/-[0.5, 1.0), rather than the usual +/-[1.0, 2.0). - if *exp == IEK_ZERO { - *exp = 0; - } else { - *exp += 1; - } - self.scalbn_r(-*exp, round) - } -} - -impl FloatConvert> for IeeeFloat { - fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd> { - let mut r = IeeeFloat { - sig: self.sig, - exp: self.exp, - category: self.category, - sign: self.sign, - marker: PhantomData, - }; - - // x86 has some unusual NaNs which cannot be represented in any other - // format; note them here. - fn is_x87_double_extended() -> bool { - S::QNAN_SIGNIFICAND == X87DoubleExtendedS::QNAN_SIGNIFICAND - } - let x87_special_nan = is_x87_double_extended::() - && !is_x87_double_extended::() - && r.category == Category::NaN - && (r.sig[0] & S::QNAN_SIGNIFICAND) != S::QNAN_SIGNIFICAND; - - // If this is a truncation of a denormal number, and the target semantics - // has larger exponent range than the source semantics (this can happen - // when truncating from PowerPC double-double to double format), the - // right shift could lose result mantissa bits. Adjust exponent instead - // of performing excessive shift. - let mut shift = T::PRECISION as ExpInt - S::PRECISION as ExpInt; - if shift < 0 && r.is_finite_non_zero() { - let mut exp_change = sig::omsb(&r.sig) as ExpInt - S::PRECISION as ExpInt; - if r.exp + exp_change < T::MIN_EXP { - exp_change = T::MIN_EXP - r.exp; - } - if exp_change < shift { - exp_change = shift; - } - if exp_change < 0 { - shift -= exp_change; - r.exp += exp_change; - } - } - - // If this is a truncation, perform the shift. - let loss = if shift < 0 && (r.is_finite_non_zero() || r.category == Category::NaN) { - sig::shift_right(&mut r.sig, &mut 0, -shift as usize) - } else { - Loss::ExactlyZero - }; - - // If this is an extension, perform the shift. - if shift > 0 && (r.is_finite_non_zero() || r.category == Category::NaN) { - sig::shift_left(&mut r.sig, &mut 0, shift as usize); - } - - let status; - if r.is_finite_non_zero() { - r = unpack!(status=, r.normalize(round, loss)); - *loses_info = status != Status::OK; - } else if r.category == Category::NaN { - *loses_info = loss != Loss::ExactlyZero || x87_special_nan; - - // For x87 extended precision, we want to make a NaN, not a special NaN if - // the input wasn't special either. - if !x87_special_nan && is_x87_double_extended::() { - sig::set_bit(&mut r.sig, T::PRECISION - 1); - } - - // Convert of sNaN creates qNaN and raises an exception (invalid op). - // This also guarantees that a sNaN does not become Inf on a truncation - // that loses all payload bits. - if self.is_signaling() { - // Quiet signaling NaN. - sig::set_bit(&mut r.sig, T::QNAN_BIT); - status = Status::INVALID_OP; - } else { - status = Status::OK; - } - } else { - *loses_info = false; - status = Status::OK; - } - - status.and(r) - } -} - -impl IeeeFloat { - /// Handle positive overflow. We either return infinity or - /// the largest finite number. For negative overflow, - /// negate the `round` argument before calling. - fn overflow_result(round: Round) -> StatusAnd { - match round { - // Infinity? - Round::NearestTiesToEven | Round::NearestTiesToAway | Round::TowardPositive => { - (Status::OVERFLOW | Status::INEXACT).and(Self::INFINITY) - } - // Otherwise we become the largest finite number. - Round::TowardNegative | Round::TowardZero => Status::INEXACT.and(Self::largest()), - } - } - - /// Returns `true` if, when truncating the current number, with `bit` the - /// new LSB, with the given lost fraction and rounding mode, the result - /// would need to be rounded away from zero (i.e., by increasing the - /// signficand). This routine must work for `Category::Zero` of both signs, and - /// `Category::Normal` numbers. - fn round_away_from_zero(&self, round: Round, loss: Loss, bit: usize) -> bool { - // NaNs and infinities should not have lost fractions. - assert!(self.is_finite_non_zero() || self.is_zero()); - - // Current callers never pass this so we don't handle it. - assert_ne!(loss, Loss::ExactlyZero); - - match round { - Round::NearestTiesToAway => loss == Loss::ExactlyHalf || loss == Loss::MoreThanHalf, - Round::NearestTiesToEven => { - if loss == Loss::MoreThanHalf { - return true; - } - - // Our zeros don't have a significand to test. - if loss == Loss::ExactlyHalf && self.category != Category::Zero { - return sig::get_bit(&self.sig, bit); - } - - false - } - Round::TowardZero => false, - Round::TowardPositive => !self.sign, - Round::TowardNegative => self.sign, - } - } - - fn normalize(mut self, round: Round, mut loss: Loss) -> StatusAnd { - if !self.is_finite_non_zero() { - return Status::OK.and(self); - } - - // Before rounding normalize the exponent of Category::Normal numbers. - let mut omsb = sig::omsb(&self.sig); - - if omsb > 0 { - // OMSB is numbered from 1. We want to place it in the integer - // bit numbered PRECISION if possible, with a compensating change in - // the exponent. - let mut final_exp = self.exp.saturating_add(omsb as ExpInt - S::PRECISION as ExpInt); - - // If the resulting exponent is too high, overflow according to - // the rounding mode. - if final_exp > S::MAX_EXP { - let round = if self.sign { -round } else { round }; - return Self::overflow_result(round).map(|r| r.copy_sign(self)); - } - - // Subnormal numbers have exponent MIN_EXP, and their MSB - // is forced based on that. - if final_exp < S::MIN_EXP { - final_exp = S::MIN_EXP; - } - - // Shifting left is easy as we don't lose precision. - if final_exp < self.exp { - assert_eq!(loss, Loss::ExactlyZero); - - let exp_change = (self.exp - final_exp) as usize; - sig::shift_left(&mut self.sig, &mut self.exp, exp_change); - - return Status::OK.and(self); - } - - // Shift right and capture any new lost fraction. - if final_exp > self.exp { - let exp_change = (final_exp - self.exp) as usize; - loss = sig::shift_right(&mut self.sig, &mut self.exp, exp_change).combine(loss); - - // Keep OMSB up-to-date. - omsb = omsb.saturating_sub(exp_change); - } - } - - // Now round the number according to round given the lost - // fraction. - - // As specified in IEEE 754, since we do not trap we do not report - // underflow for exact results. - if loss == Loss::ExactlyZero { - // Canonicalize zeros. - if omsb == 0 { - self.category = Category::Zero; - } - - return Status::OK.and(self); - } - - // Increment the significand if we're rounding away from zero. - if self.round_away_from_zero(round, loss, 0) { - if omsb == 0 { - self.exp = S::MIN_EXP; - } - - // We should never overflow. - assert_eq!(sig::increment(&mut self.sig), 0); - omsb = sig::omsb(&self.sig); - - // Did the significand increment overflow? - if omsb == S::PRECISION + 1 { - // Renormalize by incrementing the exponent and shifting our - // significand right one. However if we already have the - // maximum exponent we overflow to infinity. - if self.exp == S::MAX_EXP { - self.category = Category::Infinity; - - return (Status::OVERFLOW | Status::INEXACT).and(self); - } - - let _: Loss = sig::shift_right(&mut self.sig, &mut self.exp, 1); - - return Status::INEXACT.and(self); - } - } - - // The normal case - we were and are not denormal, and any - // significand increment above didn't overflow. - if omsb == S::PRECISION { - return Status::INEXACT.and(self); - } - - // We have a non-zero denormal. - assert!(omsb < S::PRECISION); - - // Canonicalize zeros. - if omsb == 0 { - self.category = Category::Zero; - } - - // The Category::Zero case is a denormal that underflowed to zero. - (Status::UNDERFLOW | Status::INEXACT).and(self) - } - - fn from_hexadecimal_string(s: &str, round: Round) -> Result, ParseError> { - let mut r = IeeeFloat { - sig: [0], - exp: 0, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - - let mut any_digits = false; - let mut has_exp = false; - let mut bit_pos = LIMB_BITS as isize; - let mut loss = None; - - // Without leading or trailing zeros, irrespective of the dot. - let mut first_sig_digit = None; - let mut dot = s.len(); - - for (p, c) in s.char_indices() { - // Skip leading zeros and any (hexa)decimal point. - if c == '.' { - if dot != s.len() { - return Err(ParseError("String contains multiple dots")); - } - dot = p; - } else if let Some(hex_value) = c.to_digit(16) { - any_digits = true; - - if first_sig_digit.is_none() { - if hex_value == 0 { - continue; - } - first_sig_digit = Some(p); - } - - // Store the number while we have space. - bit_pos -= 4; - if bit_pos >= 0 { - r.sig[0] |= (hex_value as Limb) << bit_pos; - // If zero or one-half (the hexadecimal digit 8) are followed - // by non-zero, they're a little more than zero or one-half. - } else if let Some(ref mut loss) = loss { - if hex_value != 0 { - if *loss == Loss::ExactlyZero { - *loss = Loss::LessThanHalf; - } - if *loss == Loss::ExactlyHalf { - *loss = Loss::MoreThanHalf; - } - } - } else { - loss = Some(match hex_value { - 0 => Loss::ExactlyZero, - 1..=7 => Loss::LessThanHalf, - 8 => Loss::ExactlyHalf, - 9..=15 => Loss::MoreThanHalf, - _ => unreachable!(), - }); - } - } else if c == 'p' || c == 'P' { - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - if dot == s.len() { - dot = p; - } - - let mut chars = s[p + 1..].chars().peekable(); - - // Adjust for the given exponent. - let exp_minus = chars.peek() == Some(&'-'); - if exp_minus || chars.peek() == Some(&'+') { - chars.next(); - } - - for c in chars { - if let Some(value) = c.to_digit(10) { - has_exp = true; - r.exp = r.exp.saturating_mul(10).saturating_add(value as ExpInt); - } else { - return Err(ParseError("Invalid character in exponent")); - } - } - if !has_exp { - return Err(ParseError("Exponent has no digits")); - } - - if exp_minus { - r.exp = -r.exp; - } - - break; - } else { - return Err(ParseError("Invalid character in significand")); - } - } - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - // Hex floats require an exponent but not a hexadecimal point. - if !has_exp { - return Err(ParseError("Hex strings require an exponent")); - } - - // Ignore the exponent if we are zero. - let first_sig_digit = match first_sig_digit { - Some(p) => p, - None => return Ok(Status::OK.and(Self::ZERO)), - }; - - // Calculate the exponent adjustment implicit in the number of - // significant digits and adjust for writing the significand starting - // at the most significant nibble. - let exp_adjustment = if dot > first_sig_digit { - ExpInt::try_from(dot - first_sig_digit).unwrap() - } else { - -ExpInt::try_from(first_sig_digit - dot - 1).unwrap() - }; - let exp_adjustment = exp_adjustment - .saturating_mul(4) - .saturating_sub(1) - .saturating_add(S::PRECISION as ExpInt) - .saturating_sub(LIMB_BITS as ExpInt); - r.exp = r.exp.saturating_add(exp_adjustment); - - Ok(r.normalize(round, loss.unwrap_or(Loss::ExactlyZero))) - } - - fn from_decimal_string(s: &str, round: Round) -> Result, ParseError> { - // Given a normal decimal floating point number of the form - // - // dddd.dddd[eE][+-]ddd - // - // where the decimal point and exponent are optional, fill out the - // variables below. Exponent is appropriate if the significand is - // treated as an integer, and normalized_exp if the significand - // is taken to have the decimal point after a single leading - // non-zero digit. - // - // If the value is zero, first_sig_digit is None. - - let mut any_digits = false; - let mut dec_exp = 0i32; - - // Without leading or trailing zeros, irrespective of the dot. - let mut first_sig_digit = None; - let mut last_sig_digit = 0; - let mut dot = s.len(); - - for (p, c) in s.char_indices() { - if c == '.' { - if dot != s.len() { - return Err(ParseError("String contains multiple dots")); - } - dot = p; - } else if let Some(dec_value) = c.to_digit(10) { - any_digits = true; - - if dec_value != 0 { - if first_sig_digit.is_none() { - first_sig_digit = Some(p); - } - last_sig_digit = p; - } - } else if c == 'e' || c == 'E' { - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - if dot == s.len() { - dot = p; - } - - let mut chars = s[p + 1..].chars().peekable(); - - // Adjust for the given exponent. - let exp_minus = chars.peek() == Some(&'-'); - if exp_minus || chars.peek() == Some(&'+') { - chars.next(); - } - - any_digits = false; - for c in chars { - if let Some(value) = c.to_digit(10) { - any_digits = true; - dec_exp = dec_exp.saturating_mul(10).saturating_add(value as i32); - } else { - return Err(ParseError("Invalid character in exponent")); - } - } - if !any_digits { - return Err(ParseError("Exponent has no digits")); - } - - if exp_minus { - dec_exp = -dec_exp; - } - - break; - } else { - return Err(ParseError("Invalid character in significand")); - } - } - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - // Test if we have a zero number allowing for non-zero exponents. - let first_sig_digit = match first_sig_digit { - Some(p) => p, - None => return Ok(Status::OK.and(Self::ZERO)), - }; - - // Adjust the exponents for any decimal point. - if dot > last_sig_digit { - dec_exp = dec_exp.saturating_add((dot - last_sig_digit - 1) as i32); - } else { - dec_exp = dec_exp.saturating_sub((last_sig_digit - dot) as i32); - } - let significand_digits = last_sig_digit - first_sig_digit + 1 - - (dot > first_sig_digit && dot < last_sig_digit) as usize; - let normalized_exp = dec_exp.saturating_add(significand_digits as i32 - 1); - - // Handle the cases where exponents are obviously too large or too - // small. Writing L for log 10 / log 2, a number d.ddddd*10^dec_exp - // definitely overflows if - // - // (dec_exp - 1) * L >= MAX_EXP - // - // and definitely underflows to zero where - // - // (dec_exp + 1) * L <= MIN_EXP - PRECISION - // - // With integer arithmetic the tightest bounds for L are - // - // 93/28 < L < 196/59 [ numerator <= 256 ] - // 42039/12655 < L < 28738/8651 [ numerator <= 65536 ] - - // Check for MAX_EXP. - if normalized_exp.saturating_sub(1).saturating_mul(42039) >= 12655 * S::MAX_EXP as i32 { - // Overflow and round. - return Ok(Self::overflow_result(round)); - } - - // Check for MIN_EXP. - if normalized_exp.saturating_add(1).saturating_mul(28738) - <= 8651 * (S::MIN_EXP as i32 - S::PRECISION as i32) - { - // Underflow to zero and round. - let r = - if round == Round::TowardPositive { IeeeFloat::SMALLEST } else { IeeeFloat::ZERO }; - return Ok((Status::UNDERFLOW | Status::INEXACT).and(r)); - } - - // A tight upper bound on number of bits required to hold an - // N-digit decimal integer is N * 196 / 59. Allocate enough space - // to hold the full significand, and an extra limb required by - // tcMultiplyPart. - let max_limbs = limbs_for_bits(1 + 196 * significand_digits / 59); - let mut dec_sig: SmallVec<[Limb; 1]> = SmallVec::with_capacity(max_limbs); - - // Convert to binary efficiently - we do almost all multiplication - // in a Limb. When this would overflow do we do a single - // bignum multiplication, and then revert again to multiplication - // in a Limb. - let mut chars = s[first_sig_digit..=last_sig_digit].chars(); - loop { - let mut val = 0; - let mut multiplier = 1; - - loop { - let dec_value = match chars.next() { - Some('.') => continue, - Some(c) => c.to_digit(10).unwrap(), - None => break, - }; - - multiplier *= 10; - val = val * 10 + dec_value as Limb; - - // The maximum number that can be multiplied by ten with any - // digit added without overflowing a Limb. - if multiplier > (!0 - 9) / 10 { - break; - } - } - - // If we've consumed no digits, we're done. - if multiplier == 1 { - break; - } - - // Multiply out the current limb. - let mut carry = val; - for x in &mut dec_sig { - let [low, mut high] = sig::widening_mul(*x, multiplier); - - // Now add carry. - let (low, overflow) = low.overflowing_add(carry); - high += overflow as Limb; - - *x = low; - carry = high; - } - - // If we had carry, we need another limb (likely but not guaranteed). - if carry > 0 { - dec_sig.push(carry); - } - } - - // Calculate pow(5, abs(dec_exp)) into `pow5_full`. - // The *_calc Vec's are reused scratch space, as an optimization. - let (pow5_full, mut pow5_calc, mut sig_calc, mut sig_scratch_calc) = { - let mut power = dec_exp.abs() as usize; - - const FIRST_EIGHT_POWERS: [Limb; 8] = [1, 5, 25, 125, 625, 3125, 15625, 78125]; - - let mut p5_scratch = smallvec![]; - let mut p5: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[4]]; - - let mut r_scratch = smallvec![]; - let mut r: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[power & 7]]; - power >>= 3; - - while power > 0 { - // Calculate pow(5,pow(2,n+3)). - p5_scratch.resize(p5.len() * 2, 0); - let _: Loss = sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS); - while p5_scratch.last() == Some(&0) { - p5_scratch.pop(); - } - mem::swap(&mut p5, &mut p5_scratch); - - if power & 1 != 0 { - r_scratch.resize(r.len() + p5.len(), 0); - let _: Loss = - sig::mul(&mut r_scratch, &mut 0, &r, &p5, (r.len() + p5.len()) * LIMB_BITS); - while r_scratch.last() == Some(&0) { - r_scratch.pop(); - } - mem::swap(&mut r, &mut r_scratch); - } - - power >>= 1; - } - - (r, r_scratch, p5, p5_scratch) - }; - - // Attempt dec_sig * 10^dec_exp with increasing precision. - let mut attempt = 0; - loop { - let calc_precision = (LIMB_BITS << attempt) - 1; - attempt += 1; - - let calc_normal_from_limbs = |sig: &mut SmallVec<[Limb; 1]>, - limbs: &[Limb]| - -> StatusAnd { - sig.resize(limbs_for_bits(calc_precision), 0); - let (mut loss, mut exp) = sig::from_limbs(sig, limbs, calc_precision); - - // Before rounding normalize the exponent of Category::Normal numbers. - let mut omsb = sig::omsb(sig); - - assert_ne!(omsb, 0); - - // OMSB is numbered from 1. We want to place it in the integer - // bit numbered PRECISION if possible, with a compensating change in - // the exponent. - let final_exp = exp.saturating_add(omsb as ExpInt - calc_precision as ExpInt); - - // Shifting left is easy as we don't lose precision. - if final_exp < exp { - assert_eq!(loss, Loss::ExactlyZero); - - let exp_change = (exp - final_exp) as usize; - sig::shift_left(sig, &mut exp, exp_change); - - return Status::OK.and(exp); - } - - // Shift right and capture any new lost fraction. - if final_exp > exp { - let exp_change = (final_exp - exp) as usize; - loss = sig::shift_right(sig, &mut exp, exp_change).combine(loss); - - // Keep OMSB up-to-date. - omsb = omsb.saturating_sub(exp_change); - } - - assert_eq!(omsb, calc_precision); - - // Now round the number according to round given the lost - // fraction. - - // As specified in IEEE 754, since we do not trap we do not report - // underflow for exact results. - if loss == Loss::ExactlyZero { - return Status::OK.and(exp); - } - - // Increment the significand if we're rounding away from zero. - if loss == Loss::MoreThanHalf || loss == Loss::ExactlyHalf && sig::get_bit(sig, 0) { - // We should never overflow. - assert_eq!(sig::increment(sig), 0); - omsb = sig::omsb(sig); - - // Did the significand increment overflow? - if omsb == calc_precision + 1 { - let _: Loss = sig::shift_right(sig, &mut exp, 1); - - return Status::INEXACT.and(exp); - } - } - - // The normal case - we were and are not denormal, and any - // significand increment above didn't overflow. - Status::INEXACT.and(exp) - }; - - let status; - let mut exp = unpack!(status=, - calc_normal_from_limbs(&mut sig_calc, &dec_sig)); - let pow5_status; - let pow5_exp = unpack!(pow5_status=, - calc_normal_from_limbs(&mut pow5_calc, &pow5_full)); - - // Add dec_exp, as 10^n = 5^n * 2^n. - exp += dec_exp as ExpInt; - - let mut used_bits = S::PRECISION; - let mut truncated_bits = calc_precision - used_bits; - - let half_ulp_err1 = (status != Status::OK) as Limb; - let (calc_loss, half_ulp_err2); - if dec_exp >= 0 { - exp += pow5_exp; - - sig_scratch_calc.resize(sig_calc.len() + pow5_calc.len(), 0); - calc_loss = sig::mul( - &mut sig_scratch_calc, - &mut exp, - &sig_calc, - &pow5_calc, - calc_precision, - ); - mem::swap(&mut sig_calc, &mut sig_scratch_calc); - - half_ulp_err2 = (pow5_status != Status::OK) as Limb; - } else { - exp -= pow5_exp; - - sig_scratch_calc.resize(sig_calc.len(), 0); - calc_loss = sig::div( - &mut sig_scratch_calc, - &mut exp, - &mut sig_calc, - &mut pow5_calc, - calc_precision, - ); - mem::swap(&mut sig_calc, &mut sig_scratch_calc); - - // Denormal numbers have less precision. - if exp < S::MIN_EXP { - truncated_bits += (S::MIN_EXP - exp) as usize; - used_bits = calc_precision.saturating_sub(truncated_bits); - } - // Extra half-ulp lost in reciprocal of exponent. - half_ulp_err2 = - 2 * (pow5_status != Status::OK || calc_loss != Loss::ExactlyZero) as Limb; - } - - // Both sig::mul and sig::div return the - // result with the integer bit set. - assert!(sig::get_bit(&sig_calc, calc_precision - 1)); - - // The error from the true value, in half-ulps, on multiplying two - // floating point numbers, which differ from the value they - // approximate by at most half_ulp_err1 and half_ulp_err2 half-ulps, is strictly less - // than the returned value. - // - // See "How to Read Floating Point Numbers Accurately" by William D Clinger. - assert!(half_ulp_err1 < 2 || half_ulp_err2 < 2 || (half_ulp_err1 + half_ulp_err2 < 8)); - - let inexact = (calc_loss != Loss::ExactlyZero) as Limb; - let half_ulp_err = if half_ulp_err1 + half_ulp_err2 == 0 { - inexact * 2 // <= inexact half-ulps. - } else { - inexact + 2 * (half_ulp_err1 + half_ulp_err2) - }; - - let ulps_from_boundary = { - let bits = calc_precision - used_bits - 1; - - let i = bits / LIMB_BITS; - let limb = sig_calc[i] & (!0 >> (LIMB_BITS - 1 - bits % LIMB_BITS)); - let boundary = match round { - Round::NearestTiesToEven | Round::NearestTiesToAway => 1 << (bits % LIMB_BITS), - _ => 0, - }; - if i == 0 { - let delta = limb.wrapping_sub(boundary); - cmp::min(delta, delta.wrapping_neg()) - } else if limb == boundary { - if !sig::is_all_zeros(&sig_calc[1..i]) { - !0 // A lot. - } else { - sig_calc[0] - } - } else if limb == boundary.wrapping_sub(1) { - if sig_calc[1..i].iter().any(|&x| x.wrapping_neg() != 1) { - !0 // A lot. - } else { - sig_calc[0].wrapping_neg() - } - } else { - !0 // A lot. - } - }; - - // Are we guaranteed to round correctly if we truncate? - if ulps_from_boundary.saturating_mul(2) >= half_ulp_err { - let mut r = IeeeFloat { - sig: [0], - exp, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - sig::extract(&mut r.sig, &sig_calc, used_bits, calc_precision - used_bits); - // If we extracted less bits above we must adjust our exponent - // to compensate for the implicit right shift. - r.exp += (S::PRECISION - used_bits) as ExpInt; - let loss = Loss::through_truncation(&sig_calc, truncated_bits); - return Ok(r.normalize(round, loss)); - } - } - } -} - -impl Loss { - /// Combine the effect of two lost fractions. - fn combine(self, less_significant: Loss) -> Loss { - let mut more_significant = self; - if less_significant != Loss::ExactlyZero { - if more_significant == Loss::ExactlyZero { - more_significant = Loss::LessThanHalf; - } else if more_significant == Loss::ExactlyHalf { - more_significant = Loss::MoreThanHalf; - } - } - - more_significant - } - - /// Returns the fraction lost were a bignum truncated losing the least - /// significant `bits` bits. - fn through_truncation(limbs: &[Limb], bits: usize) -> Loss { - if bits == 0 { - return Loss::ExactlyZero; - } - - let half_bit = bits - 1; - let half_limb = half_bit / LIMB_BITS; - let (half_limb, rest) = if half_limb < limbs.len() { - (limbs[half_limb], &limbs[..half_limb]) - } else { - (0, limbs) - }; - let half = 1 << (half_bit % LIMB_BITS); - let has_half = half_limb & half != 0; - let has_rest = half_limb & (half - 1) != 0 || !sig::is_all_zeros(rest); - - match (has_half, has_rest) { - (false, false) => Loss::ExactlyZero, - (false, true) => Loss::LessThanHalf, - (true, false) => Loss::ExactlyHalf, - (true, true) => Loss::MoreThanHalf, - } - } -} - -/// Implementation details of IeeeFloat significands, such as big integer arithmetic. -/// As a rule of thumb, no functions in this module should dynamically allocate. -mod sig { - use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS}; - use core::cmp::Ordering; - use core::iter; - use core::mem; - - pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool { - limbs.iter().all(|&l| l == 0) - } - - /// One, not zero, based LSB. That is, returns 0 for a zeroed significand. - pub(super) fn olsb(limbs: &[Limb]) -> usize { - limbs - .iter() - .enumerate() - .find(|(_, &limb)| limb != 0) - .map_or(0, |(i, limb)| i * LIMB_BITS + limb.trailing_zeros() as usize + 1) - } - - /// One, not zero, based MSB. That is, returns 0 for a zeroed significand. - pub(super) fn omsb(limbs: &[Limb]) -> usize { - limbs - .iter() - .enumerate() - .rfind(|(_, &limb)| limb != 0) - .map_or(0, |(i, limb)| (i + 1) * LIMB_BITS - limb.leading_zeros() as usize) - } - - /// Comparison (unsigned) of two significands. - pub(super) fn cmp(a: &[Limb], b: &[Limb]) -> Ordering { - assert_eq!(a.len(), b.len()); - for (a, b) in a.iter().zip(b).rev() { - match a.cmp(b) { - Ordering::Equal => {} - o => return o, - } - } - - Ordering::Equal - } - - /// Extracts the given bit. - pub(super) fn get_bit(limbs: &[Limb], bit: usize) -> bool { - limbs[bit / LIMB_BITS] & (1 << (bit % LIMB_BITS)) != 0 - } - - /// Sets the given bit. - pub(super) fn set_bit(limbs: &mut [Limb], bit: usize) { - limbs[bit / LIMB_BITS] |= 1 << (bit % LIMB_BITS); - } - - /// Clear the given bit. - pub(super) fn clear_bit(limbs: &mut [Limb], bit: usize) { - limbs[bit / LIMB_BITS] &= !(1 << (bit % LIMB_BITS)); - } - - /// Shifts `dst` left `bits` bits, subtract `bits` from its exponent. - pub(super) fn shift_left(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) { - if bits > 0 { - // Our exponent should not underflow. - *exp = exp.checked_sub(bits as ExpInt).unwrap(); - - // Jump is the inter-limb jump; shift is the intra-limb shift. - let jump = bits / LIMB_BITS; - let shift = bits % LIMB_BITS; - - for i in (0..dst.len()).rev() { - let mut limb; - - if i < jump { - limb = 0; - } else { - // dst[i] comes from the two limbs src[i - jump] and, if we have - // an intra-limb shift, src[i - jump - 1]. - limb = dst[i - jump]; - if shift > 0 { - limb <<= shift; - if i > jump { - limb |= dst[i - jump - 1] >> (LIMB_BITS - shift); - } - } - } - - dst[i] = limb; - } - } - } - - /// Shifts `dst` right `bits` bits noting lost fraction. - pub(super) fn shift_right(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) -> Loss { - let loss = Loss::through_truncation(dst, bits); - - if bits > 0 { - // Our exponent should not overflow. - *exp = exp.checked_add(bits as ExpInt).unwrap(); - - // Jump is the inter-limb jump; shift is the intra-limb shift. - let jump = bits / LIMB_BITS; - let shift = bits % LIMB_BITS; - - // Perform the shift. This leaves the most significant `bits` bits - // of the result at zero. - for i in 0..dst.len() { - let mut limb; - - if i + jump >= dst.len() { - limb = 0; - } else { - limb = dst[i + jump]; - if shift > 0 { - limb >>= shift; - if i + jump + 1 < dst.len() { - limb |= dst[i + jump + 1] << (LIMB_BITS - shift); - } - } - } - - dst[i] = limb; - } - } - - loss - } - - /// Copies the bit vector of width `src_bits` from `src`, starting at bit SRC_LSB, - /// to `dst`, such that the bit SRC_LSB becomes the least significant bit of `dst`. - /// All high bits above `src_bits` in `dst` are zero-filled. - pub(super) fn extract(dst: &mut [Limb], src: &[Limb], src_bits: usize, src_lsb: usize) { - if src_bits == 0 { - return; - } - - let dst_limbs = limbs_for_bits(src_bits); - assert!(dst_limbs <= dst.len()); - - let src = &src[src_lsb / LIMB_BITS..]; - dst[..dst_limbs].copy_from_slice(&src[..dst_limbs]); - - let shift = src_lsb % LIMB_BITS; - let _: Loss = shift_right(&mut dst[..dst_limbs], &mut 0, shift); - - // We now have (dst_limbs * LIMB_BITS - shift) bits from `src` - // in `dst`. If this is less that src_bits, append the rest, else - // clear the high bits. - let n = dst_limbs * LIMB_BITS - shift; - if n < src_bits { - let mask = (1 << (src_bits - n)) - 1; - dst[dst_limbs - 1] |= (src[dst_limbs] & mask) << (n % LIMB_BITS); - } else if n > src_bits && src_bits % LIMB_BITS > 0 { - dst[dst_limbs - 1] &= (1 << (src_bits % LIMB_BITS)) - 1; - } - - // Clear high limbs. - for x in &mut dst[dst_limbs..] { - *x = 0; - } - } - - /// We want the most significant PRECISION bits of `src`. There may not - /// be that many; extract what we can. - pub(super) fn from_limbs(dst: &mut [Limb], src: &[Limb], precision: usize) -> (Loss, ExpInt) { - let omsb = omsb(src); - - if precision <= omsb { - extract(dst, src, precision, omsb - precision); - (Loss::through_truncation(src, omsb - precision), omsb as ExpInt - 1) - } else { - extract(dst, src, omsb, 0); - (Loss::ExactlyZero, precision as ExpInt - 1) - } - } - - /// For every consecutive chunk of `bits` bits from `limbs`, - /// going from most significant to the least significant bits, - /// call `f` to transform those bits and store the result back. - pub(super) fn each_chunk Limb>(limbs: &mut [Limb], bits: usize, mut f: F) { - assert_eq!(LIMB_BITS % bits, 0); - for limb in limbs.iter_mut().rev() { - let mut r = 0; - for i in (0..LIMB_BITS / bits).rev() { - r |= f((*limb >> (i * bits)) & ((1 << bits) - 1)) << (i * bits); - } - *limb = r; - } - } - - /// Increment in-place, return the carry flag. - pub(super) fn increment(dst: &mut [Limb]) -> Limb { - for x in dst { - *x = x.wrapping_add(1); - if *x != 0 { - return 0; - } - } - - 1 - } - - /// Decrement in-place, return the borrow flag. - pub(super) fn decrement(dst: &mut [Limb]) -> Limb { - for x in dst { - *x = x.wrapping_sub(1); - if *x != !0 { - return 0; - } - } - - 1 - } - - /// `a += b + c` where `c` is zero or one. Returns the carry flag. - pub(super) fn add(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { - assert!(c <= 1); - - for (a, &b) in iter::zip(a, b) { - let (r, overflow) = a.overflowing_add(b); - let (r, overflow2) = r.overflowing_add(c); - *a = r; - c = (overflow | overflow2) as Limb; - } - - c - } - - /// `a -= b + c` where `c` is zero or one. Returns the borrow flag. - pub(super) fn sub(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { - assert!(c <= 1); - - for (a, &b) in iter::zip(a, b) { - let (r, overflow) = a.overflowing_sub(b); - let (r, overflow2) = r.overflowing_sub(c); - *a = r; - c = (overflow | overflow2) as Limb; - } - - c - } - - /// `a += b` or `a -= b`. Does not preserve `b`. - pub(super) fn add_or_sub( - a_sig: &mut [Limb], - a_exp: &mut ExpInt, - a_sign: &mut bool, - b_sig: &mut [Limb], - b_exp: ExpInt, - b_sign: bool, - ) -> Loss { - // Are we bigger exponent-wise than the RHS? - let bits = *a_exp - b_exp; - - // Determine if the operation on the absolute values is effectively - // an addition or subtraction. - // Subtraction is more subtle than one might naively expect. - if *a_sign ^ b_sign { - let (reverse, loss); - - if bits == 0 { - reverse = cmp(a_sig, b_sig) == Ordering::Less; - loss = Loss::ExactlyZero; - } else if bits > 0 { - loss = shift_right(b_sig, &mut 0, (bits - 1) as usize); - shift_left(a_sig, a_exp, 1); - reverse = false; - } else { - loss = shift_right(a_sig, a_exp, (-bits - 1) as usize); - shift_left(b_sig, &mut 0, 1); - reverse = true; - } - - let borrow = (loss != Loss::ExactlyZero) as Limb; - if reverse { - // The code above is intended to ensure that no borrow is necessary. - assert_eq!(sub(b_sig, a_sig, borrow), 0); - a_sig.copy_from_slice(b_sig); - *a_sign = !*a_sign; - } else { - // The code above is intended to ensure that no borrow is necessary. - assert_eq!(sub(a_sig, b_sig, borrow), 0); - } - - // Invert the lost fraction - it was on the RHS and subtracted. - match loss { - Loss::LessThanHalf => Loss::MoreThanHalf, - Loss::MoreThanHalf => Loss::LessThanHalf, - _ => loss, - } - } else { - let loss = if bits > 0 { - shift_right(b_sig, &mut 0, bits as usize) - } else { - shift_right(a_sig, a_exp, -bits as usize) - }; - // We have a guard bit; generating a carry cannot happen. - assert_eq!(add(a_sig, b_sig, 0), 0); - loss - } - } - - /// `[low, high] = a * b`. - /// - /// This cannot overflow, because - /// - /// `(n - 1) * (n - 1) + 2 * (n - 1) == (n - 1) * (n + 1)` - /// - /// which is less than n2. - pub(super) fn widening_mul(a: Limb, b: Limb) -> [Limb; 2] { - let mut wide = [0, 0]; - - if a == 0 || b == 0 { - return wide; - } - - const HALF_BITS: usize = LIMB_BITS / 2; - - let select = |limb, i| (limb >> (i * HALF_BITS)) & ((1 << HALF_BITS) - 1); - for i in 0..2 { - for j in 0..2 { - let mut x = [select(a, i) * select(b, j), 0]; - shift_left(&mut x, &mut 0, (i + j) * HALF_BITS); - assert_eq!(add(&mut wide, &x, 0), 0); - } - } - - wide - } - - /// `dst = a * b` (for normal `a` and `b`). Returns the lost fraction. - pub(super) fn mul<'a>( - dst: &mut [Limb], - exp: &mut ExpInt, - mut a: &'a [Limb], - mut b: &'a [Limb], - precision: usize, - ) -> Loss { - // Put the narrower number on the `a` for less loops below. - if a.len() > b.len() { - mem::swap(&mut a, &mut b); - } - - for x in &mut dst[..b.len()] { - *x = 0; - } - - for i in 0..a.len() { - let mut carry = 0; - for j in 0..b.len() { - let [low, mut high] = widening_mul(a[i], b[j]); - - // Now add carry. - let (low, overflow) = low.overflowing_add(carry); - high += overflow as Limb; - - // And now `dst[i + j]`, and store the new low part there. - let (low, overflow) = low.overflowing_add(dst[i + j]); - high += overflow as Limb; - - dst[i + j] = low; - carry = high; - } - dst[i + b.len()] = carry; - } - - // Assume the operands involved in the multiplication are single-precision - // FP, and the two multiplicants are: - // a = a23 . a22 ... a0 * 2^e1 - // b = b23 . b22 ... b0 * 2^e2 - // the result of multiplication is: - // dst = c48 c47 c46 . c45 ... c0 * 2^(e1+e2) - // Note that there are three significant bits at the left-hand side of the - // radix point: two for the multiplication, and an overflow bit for the - // addition (that will always be zero at this point). Move the radix point - // toward left by two bits, and adjust exponent accordingly. - *exp += 2; - - // Convert the result having "2 * precision" significant-bits back to the one - // having "precision" significant-bits. First, move the radix point from - // poision "2*precision - 1" to "precision - 1". The exponent need to be - // adjusted by "2*precision - 1" - "precision - 1" = "precision". - *exp -= precision as ExpInt + 1; - - // In case MSB resides at the left-hand side of radix point, shift the - // mantissa right by some amount to make sure the MSB reside right before - // the radix point (i.e., "MSB . rest-significant-bits"). - // - // Note that the result is not normalized when "omsb < precision". So, the - // caller needs to call IeeeFloat::normalize() if normalized value is - // expected. - let omsb = omsb(dst); - if omsb <= precision { Loss::ExactlyZero } else { shift_right(dst, exp, omsb - precision) } - } - - /// `quotient = dividend / divisor`. Returns the lost fraction. - /// Does not preserve `dividend` or `divisor`. - pub(super) fn div( - quotient: &mut [Limb], - exp: &mut ExpInt, - dividend: &mut [Limb], - divisor: &mut [Limb], - precision: usize, - ) -> Loss { - // Normalize the divisor. - let bits = precision - omsb(divisor); - shift_left(divisor, &mut 0, bits); - *exp += bits as ExpInt; - - // Normalize the dividend. - let bits = precision - omsb(dividend); - shift_left(dividend, exp, bits); - - // Division by 1. - let olsb_divisor = olsb(divisor); - if olsb_divisor == precision { - quotient.copy_from_slice(dividend); - return Loss::ExactlyZero; - } - - // Ensure the dividend >= divisor initially for the loop below. - // Incidentally, this means that the division loop below is - // guaranteed to set the integer bit to one. - if cmp(dividend, divisor) == Ordering::Less { - shift_left(dividend, exp, 1); - assert_ne!(cmp(dividend, divisor), Ordering::Less) - } - - // Helper for figuring out the lost fraction. - let lost_fraction = |dividend: &[Limb], divisor: &[Limb]| match cmp(dividend, divisor) { - Ordering::Greater => Loss::MoreThanHalf, - Ordering::Equal => Loss::ExactlyHalf, - Ordering::Less => { - if is_all_zeros(dividend) { - Loss::ExactlyZero - } else { - Loss::LessThanHalf - } - } - }; - - // Try to perform a (much faster) short division for small divisors. - let divisor_bits = precision - (olsb_divisor - 1); - macro_rules! try_short_div { - ($W:ty, $H:ty, $half:expr) => { - if divisor_bits * 2 <= $half { - // Extract the small divisor. - let _: Loss = shift_right(divisor, &mut 0, olsb_divisor - 1); - let divisor = divisor[0] as $H as $W; - - // Shift the dividend to produce a quotient with the unit bit set. - let top_limb = *dividend.last().unwrap(); - let mut rem = (top_limb >> (LIMB_BITS - (divisor_bits - 1))) as $H; - shift_left(dividend, &mut 0, divisor_bits - 1); - - // Apply short division in place on $H (of $half bits) chunks. - each_chunk(dividend, $half, |chunk| { - let chunk = chunk as $H; - let combined = ((rem as $W) << $half) | (chunk as $W); - rem = (combined % divisor) as $H; - (combined / divisor) as $H as Limb - }); - quotient.copy_from_slice(dividend); - - return lost_fraction(&[(rem as Limb) << 1], &[divisor as Limb]); - } - }; - } - - try_short_div!(u32, u16, 16); - try_short_div!(u64, u32, 32); - try_short_div!(u128, u64, 64); - - // Zero the quotient before setting bits in it. - for x in &mut quotient[..limbs_for_bits(precision)] { - *x = 0; - } - - // Long division. - for bit in (0..precision).rev() { - if cmp(dividend, divisor) != Ordering::Less { - sub(dividend, divisor, 0); - set_bit(quotient, bit); - } - shift_left(dividend, &mut 0, 1); - } - - lost_fraction(dividend, divisor) - } -} diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs deleted file mode 100644 index dde368e7b92..00000000000 --- a/compiler/rustc_apfloat/src/lib.rs +++ /dev/null @@ -1,695 +0,0 @@ -//! Port of LLVM's APFloat software floating-point implementation from the -//! following C++ sources (please update commit hash when backporting): -//! -//! -//! * `include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits -//! * `lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules -//! * `unittests/ADT/APFloatTest.cpp` -> `tests` directory -//! -//! The port contains no unsafe code, global state, or side-effects in general, -//! and the only allocations are in the conversion to/from decimal strings. -//! -//! Most of the API and the testcases are intact in some form or another, -//! with some ergonomic changes, such as idiomatic short names, returning -//! new values instead of mutating the receiver, and having separate method -//! variants that take a non-default rounding mode (with the suffix `_r`). -//! Comments have been preserved where possible, only slightly adapted. -//! -//! Instead of keeping a pointer to a configuration struct and inspecting it -//! dynamically on every operation, types (e.g., `ieee::Double`), traits -//! (e.g., `ieee::Semantics`) and associated constants are employed for -//! increased type safety and performance. -//! -//! On-heap bigints are replaced everywhere (except in decimal conversion), -//! with short arrays of `type Limb = u128` elements (instead of `u64`), -//! This allows fitting the largest supported significands in one integer -//! (`ieee::Quad` and `ppc::Fallback` use slightly less than 128 bits). -//! All of the functions in the `ieee::sig` module operate on slices. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![no_std] -#![forbid(unsafe_code)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] - -#[macro_use] -extern crate alloc; - -use core::cmp::Ordering; -use core::fmt; -use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; -use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; -use core::str::FromStr; - -bitflags::bitflags! { - /// IEEE-754R 7: Default exception handling. - /// - /// UNDERFLOW or OVERFLOW are always returned or-ed with INEXACT. - #[must_use] - pub struct Status: u8 { - const OK = 0x00; - const INVALID_OP = 0x01; - const DIV_BY_ZERO = 0x02; - const OVERFLOW = 0x04; - const UNDERFLOW = 0x08; - const INEXACT = 0x10; - } -} - -#[must_use] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct StatusAnd { - pub status: Status, - pub value: T, -} - -impl Status { - pub fn and(self, value: T) -> StatusAnd { - StatusAnd { status: self, value } - } -} - -impl StatusAnd { - pub fn map U, U>(self, f: F) -> StatusAnd { - StatusAnd { status: self.status, value: f(self.value) } - } -} - -#[macro_export] -macro_rules! unpack { - ($status:ident|=, $e:expr) => { - match $e { - $crate::StatusAnd { status, value } => { - $status |= status; - value - } - } - }; - ($status:ident=, $e:expr) => { - match $e { - $crate::StatusAnd { status, value } => { - $status = status; - value - } - } - }; -} - -/// Category of internally-represented number. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Category { - Infinity, - NaN, - Normal, - Zero, -} - -/// IEEE-754R 4.3: Rounding-direction attributes. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Round { - NearestTiesToEven, - TowardPositive, - TowardNegative, - TowardZero, - NearestTiesToAway, -} - -impl Neg for Round { - type Output = Round; - fn neg(self) -> Round { - match self { - Round::TowardPositive => Round::TowardNegative, - Round::TowardNegative => Round::TowardPositive, - Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self, - } - } -} - -/// A signed type to represent a floating point number's unbiased exponent. -pub type ExpInt = i16; - -// \c ilogb error results. -pub const IEK_INF: ExpInt = ExpInt::MAX; -pub const IEK_NAN: ExpInt = ExpInt::MIN; -pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct ParseError(pub &'static str); - -/// A self-contained host- and target-independent arbitrary-precision -/// floating-point software implementation. -/// -/// `apfloat` uses significand bignum integer arithmetic as provided by functions -/// in the `ieee::sig`. -/// -/// Written for clarity rather than speed, in particular with a view to use in -/// the front-end of a cross compiler so that target arithmetic can be correctly -/// performed on the host. Performance should nonetheless be reasonable, -/// particularly for its intended use. It may be useful as a base -/// implementation for a run-time library during development of a faster -/// target-specific one. -/// -/// All 5 rounding modes in the IEEE-754R draft are handled correctly for all -/// implemented operations. Currently implemented operations are add, subtract, -/// multiply, divide, fused-multiply-add, conversion-to-float, -/// conversion-to-integer and conversion-from-integer. New rounding modes -/// (e.g., away from zero) can be added with three or four lines of code. -/// -/// Four formats are built-in: IEEE single precision, double precision, -/// quadruple precision, and x87 80-bit extended double (when operating with -/// full extended precision). Adding a new format that obeys IEEE semantics -/// only requires adding two lines of code: a declaration and definition of the -/// format. -/// -/// All operations return the status of that operation as an exception bit-mask, -/// so multiple operations can be done consecutively with their results or-ed -/// together. The returned status can be useful for compiler diagnostics; e.g., -/// inexact, underflow and overflow can be easily diagnosed on constant folding, -/// and compiler optimizers can determine what exceptions would be raised by -/// folding operations and optimize, or perhaps not optimize, accordingly. -/// -/// At present, underflow tininess is detected after rounding; it should be -/// straight forward to add support for the before-rounding case too. -/// -/// The library reads hexadecimal floating point numbers as per C99, and -/// correctly rounds if necessary according to the specified rounding mode. -/// Syntax is required to have been validated by the caller. -/// -/// It also reads decimal floating point numbers and correctly rounds according -/// to the specified rounding mode. -/// -/// Non-zero finite numbers are represented internally as a sign bit, a 16-bit -/// signed exponent, and the significand as an array of integer limbs. After -/// normalization of a number of precision P the exponent is within the range of -/// the format, and if the number is not denormal the P-th bit of the -/// significand is set as an explicit integer bit. For denormals the most -/// significant bit is shifted right so that the exponent is maintained at the -/// format's minimum, so that the smallest denormal has just the least -/// significant bit of the significand set. The sign of zeros and infinities -/// is significant; the exponent and significand of such numbers is not stored, -/// but has a known implicit (deterministic) value: 0 for the significands, 0 -/// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and -/// significand are deterministic, although not really meaningful, and preserved -/// in non-conversion operations. The exponent is implicitly all 1 bits. -/// -/// `apfloat` does not provide any exception handling beyond default exception -/// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause -/// by encoding Signaling NaNs with the first bit of its trailing significand -/// as 0. -/// -/// Future work -/// =========== -/// -/// Some features that may or may not be worth adding: -/// -/// Optional ability to detect underflow tininess before rounding. -/// -/// New formats: x87 in single and double precision mode (IEEE apart from -/// extended exponent range) (hard). -/// -/// New operations: sqrt, nexttoward. -/// -pub trait Float: - Copy - + Default - + FromStr - + PartialOrd - + fmt::Display - + Neg - + AddAssign - + SubAssign - + MulAssign - + DivAssign - + RemAssign - + Add> - + Sub> - + Mul> - + Div> - + Rem> -{ - /// Total number of bits in the in-memory format. - const BITS: usize; - - /// Number of bits in the significand. This includes the integer bit. - const PRECISION: usize; - - /// The largest E such that 2E is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2E is a normalized number; this - /// matches the definition of IEEE 754. - const MIN_EXP: ExpInt; - - /// Positive Zero. - const ZERO: Self; - - /// Positive Infinity. - const INFINITY: Self; - - /// NaN (Not a Number). - // FIXME(eddyb) provide a default when qnan becomes const fn. - const NAN: Self; - - /// Factory for QNaN values. - // FIXME(eddyb) should be const fn. - fn qnan(payload: Option) -> Self; - - /// Factory for SNaN values. - // FIXME(eddyb) should be const fn. - fn snan(payload: Option) -> Self; - - /// Largest finite number. - // FIXME(eddyb) should be const (but FloatPair::largest is nontrivial). - fn largest() -> Self; - - /// Smallest (by magnitude) finite number. - /// Might be denormalized, which implies a relative loss of precision. - const SMALLEST: Self; - - /// Smallest (by magnitude) normalized finite number. - // FIXME(eddyb) should be const (but FloatPair::smallest_normalized is nontrivial). - fn smallest_normalized() -> Self; - - // Arithmetic - - fn add_r(self, rhs: Self, round: Round) -> StatusAnd; - fn sub_r(self, rhs: Self, round: Round) -> StatusAnd { - self.add_r(-rhs, round) - } - fn mul_r(self, rhs: Self, round: Round) -> StatusAnd; - fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd; - fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd { - self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven) - } - fn div_r(self, rhs: Self, round: Round) -> StatusAnd; - /// IEEE remainder. - // This is not currently correct in all cases. - fn ieee_rem(self, rhs: Self) -> StatusAnd { - let mut v = self; - - let status; - v = unpack!(status=, v / rhs); - if status == Status::DIV_BY_ZERO { - return status.and(self); - } - - assert!(Self::PRECISION < 128); - - let status; - let x = unpack!(status=, v.to_i128_r(128, Round::NearestTiesToEven, &mut false)); - if status == Status::INVALID_OP { - return status.and(self); - } - - let status; - let mut v = unpack!(status=, Self::from_i128(x)); - assert_eq!(status, Status::OK); // should always work - - let status; - v = unpack!(status=, v * rhs); - assert_eq!(status - Status::INEXACT, Status::OK); // should not overflow or underflow - - let status; - v = unpack!(status=, self - v); - assert_eq!(status - Status::INEXACT, Status::OK); // likewise - - if v.is_zero() { - status.and(v.copy_sign(self)) // IEEE754 requires this - } else { - status.and(v) - } - } - /// C fmod, or llvm frem. - fn c_fmod(self, rhs: Self) -> StatusAnd; - fn round_to_integral(self, round: Round) -> StatusAnd; - - /// IEEE-754R 2008 5.3.1: nextUp. - fn next_up(self) -> StatusAnd; - - /// IEEE-754R 2008 5.3.1: nextDown. - /// - /// *NOTE* since nextDown(x) = -nextUp(-x), we only implement nextUp with - /// appropriate sign switching before/after the computation. - fn next_down(self) -> StatusAnd { - (-self).next_up().map(|r| -r) - } - - fn abs(self) -> Self { - if self.is_negative() { -self } else { self } - } - fn copy_sign(self, rhs: Self) -> Self { - if self.is_negative() != rhs.is_negative() { -self } else { self } - } - - // Conversions - fn from_bits(input: u128) -> Self; - fn from_i128_r(input: i128, round: Round) -> StatusAnd { - if input < 0 { - Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r) - } else { - Self::from_u128_r(input as u128, round) - } - } - fn from_i128(input: i128) -> StatusAnd { - Self::from_i128_r(input, Round::NearestTiesToEven) - } - fn from_u128_r(input: u128, round: Round) -> StatusAnd; - fn from_u128(input: u128) -> StatusAnd { - Self::from_u128_r(input, Round::NearestTiesToEven) - } - fn from_str_r(s: &str, round: Round) -> Result, ParseError>; - fn to_bits(self) -> u128; - - /// Converts a floating point number to an integer according to the - /// rounding mode. In case of an invalid operation exception, - /// deterministic values are returned, namely zero for NaNs and the - /// minimal or maximal value respectively for underflow or overflow. - /// If the rounded value is in range but the floating point number is - /// not the exact integer, the C standard doesn't require an inexact - /// exception to be raised. IEEE-854 does require it so we do that. - /// - /// Note that for conversions to integer type the C standard requires - /// round-to-zero to always be used. - /// - /// The *is_exact output tells whether the result is exact, in the sense - /// that converting it back to the original floating point type produces - /// the original value. This is almost equivalent to `result == Status::OK`, - /// except for negative zeroes. - fn to_i128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - let status; - if self.is_negative() { - if self.is_zero() { - // Negative zero can't be represented as an int. - *is_exact = false; - } - let r = unpack!(status=, (-self).to_u128_r(width, -round, is_exact)); - - // Check for values that don't fit in the signed integer. - if r > (1 << (width - 1)) { - // Return the most negative integer for the given width. - *is_exact = false; - Status::INVALID_OP.and(-1 << (width - 1)) - } else { - status.and(r.wrapping_neg() as i128) - } - } else { - // Positive case is simpler, can pretend it's a smaller unsigned - // integer, and `to_u128` will take care of all the edge cases. - self.to_u128_r(width - 1, round, is_exact).map(|r| r as i128) - } - } - fn to_i128(self, width: usize) -> StatusAnd { - self.to_i128_r(width, Round::TowardZero, &mut true) - } - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd; - fn to_u128(self, width: usize) -> StatusAnd { - self.to_u128_r(width, Round::TowardZero, &mut true) - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering; - - /// Bitwise comparison for equality (QNaNs compare equal, 0!=-0). - fn bitwise_eq(self, rhs: Self) -> bool; - - // IEEE-754R 5.7.2 General operations. - - /// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if - /// both are not NaN. If either argument is a NaN, returns the other argument. - fn min(self, other: Self) -> Self { - if self.is_nan() { - other - } else if other.is_nan() { - self - } else if other.partial_cmp(&self) == Some(Ordering::Less) { - other - } else { - self - } - } - - /// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if - /// both are not NaN. If either argument is a NaN, returns the other argument. - fn max(self, other: Self) -> Self { - if self.is_nan() { - other - } else if other.is_nan() { - self - } else if self.partial_cmp(&other) == Some(Ordering::Less) { - other - } else { - self - } - } - - /// IEEE-754R isSignMinus: Returns whether the current value is - /// negative. - /// - /// This applies to zeros and NaNs as well. - fn is_negative(self) -> bool; - - /// IEEE-754R isNormal: Returns whether the current value is normal. - /// - /// This implies that the current value of the float is not zero, subnormal, - /// infinite, or NaN following the definition of normality from IEEE-754R. - fn is_normal(self) -> bool { - !self.is_denormal() && self.is_finite_non_zero() - } - - /// Returns `true` if the current value is zero, subnormal, or - /// normal. - /// - /// This means that the value is not infinite or NaN. - fn is_finite(self) -> bool { - !self.is_nan() && !self.is_infinite() - } - - /// Returns `true` if the float is plus or minus zero. - fn is_zero(self) -> bool { - self.category() == Category::Zero - } - - /// IEEE-754R isSubnormal(): Returns whether the float is a - /// denormal. - fn is_denormal(self) -> bool; - - /// IEEE-754R isInfinite(): Returns whether the float is infinity. - fn is_infinite(self) -> bool { - self.category() == Category::Infinity - } - - /// Returns `true` if the float is a quiet or signaling NaN. - fn is_nan(self) -> bool { - self.category() == Category::NaN - } - - /// Returns `true` if the float is a signaling NaN. - fn is_signaling(self) -> bool; - - // Simple Queries - - fn category(self) -> Category; - fn is_non_zero(self) -> bool { - !self.is_zero() - } - fn is_finite_non_zero(self) -> bool { - self.is_finite() && !self.is_zero() - } - fn is_pos_zero(self) -> bool { - self.is_zero() && !self.is_negative() - } - fn is_neg_zero(self) -> bool { - self.is_zero() && self.is_negative() - } - - /// Returns `true` if the number has the smallest possible non-zero - /// magnitude in the current semantics. - fn is_smallest(self) -> bool { - Self::SMALLEST.copy_sign(self).bitwise_eq(self) - } - - /// Returns `true` if the number has the largest possible finite - /// magnitude in the current semantics. - fn is_largest(self) -> bool { - Self::largest().copy_sign(self).bitwise_eq(self) - } - - /// Returns `true` if the number is an exact integer. - fn is_integer(self) -> bool { - // This could be made more efficient; I'm going for obviously correct. - if !self.is_finite() { - return false; - } - self.round_to_integral(Round::TowardZero).value.bitwise_eq(self) - } - - /// If this value has an exact multiplicative inverse, return it. - fn get_exact_inverse(self) -> Option; - - /// Returns the exponent of the internal representation of the Float. - /// - /// Because the radix of Float is 2, this is equivalent to floor(log2(x)). - /// For special Float values, this returns special error codes: - /// - /// NaN -> \c IEK_NAN - /// 0 -> \c IEK_ZERO - /// Inf -> \c IEK_INF - /// - fn ilogb(self) -> ExpInt; - - /// Returns: self * 2exp for integral exponents. - /// Equivalent to C standard library function `ldexp`. - fn scalbn_r(self, exp: ExpInt, round: Round) -> Self; - fn scalbn(self, exp: ExpInt) -> Self { - self.scalbn_r(exp, Round::NearestTiesToEven) - } - - /// Equivalent to C standard library function with the same name. - /// - /// While the C standard says exp is an unspecified value for infinity and nan, - /// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`). - fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self; - fn frexp(self, exp: &mut ExpInt) -> Self { - self.frexp_r(exp, Round::NearestTiesToEven) - } -} - -pub trait FloatConvert: Float { - /// Converts a value of one floating point type to another. - /// The return value corresponds to the IEEE754 exceptions. *loses_info - /// records whether the transformation lost information, i.e., whether - /// converting the result back to the original type will produce the - /// original value (this is almost the same as return `value == Status::OK`, - /// but there are edge cases where this is not so). - fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd; - fn convert(self, loses_info: &mut bool) -> StatusAnd { - self.convert_r(Round::NearestTiesToEven, loses_info) - } -} - -macro_rules! float_common_impls { - ($ty:ident<$t:tt>) => { - impl<$t> Default for $ty<$t> - where - Self: Float, - { - fn default() -> Self { - Self::ZERO - } - } - - impl<$t> ::core::str::FromStr for $ty<$t> - where - Self: Float, - { - type Err = ParseError; - fn from_str(s: &str) -> Result { - Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value) - } - } - - // Rounding ties to the nearest even, by default. - - impl<$t> ::core::ops::Add for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn add(self, rhs: Self) -> StatusAnd { - self.add_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Sub for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn sub(self, rhs: Self) -> StatusAnd { - self.sub_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Mul for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn mul(self, rhs: Self) -> StatusAnd { - self.mul_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Div for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn div(self, rhs: Self) -> StatusAnd { - self.div_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Rem for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn rem(self, rhs: Self) -> StatusAnd { - self.c_fmod(rhs) - } - } - - impl<$t> ::core::ops::AddAssign for $ty<$t> - where - Self: Float, - { - fn add_assign(&mut self, rhs: Self) { - *self = (*self + rhs).value; - } - } - - impl<$t> ::core::ops::SubAssign for $ty<$t> - where - Self: Float, - { - fn sub_assign(&mut self, rhs: Self) { - *self = (*self - rhs).value; - } - } - - impl<$t> ::core::ops::MulAssign for $ty<$t> - where - Self: Float, - { - fn mul_assign(&mut self, rhs: Self) { - *self = (*self * rhs).value; - } - } - - impl<$t> ::core::ops::DivAssign for $ty<$t> - where - Self: Float, - { - fn div_assign(&mut self, rhs: Self) { - *self = (*self / rhs).value; - } - } - - impl<$t> ::core::ops::RemAssign for $ty<$t> - where - Self: Float, - { - fn rem_assign(&mut self, rhs: Self) { - *self = (*self % rhs).value; - } - } - }; -} - -pub mod ieee; -pub mod ppc; diff --git a/compiler/rustc_apfloat/src/ppc.rs b/compiler/rustc_apfloat/src/ppc.rs deleted file mode 100644 index 65a0f66645b..00000000000 --- a/compiler/rustc_apfloat/src/ppc.rs +++ /dev/null @@ -1,434 +0,0 @@ -use crate::ieee; -use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd}; - -use core::cmp::Ordering; -use core::fmt; -use core::ops::Neg; - -#[must_use] -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] -pub struct DoubleFloat(F, F); -pub type DoubleDouble = DoubleFloat; - -// These are legacy semantics for the Fallback, inaccurate implementation of -// IBM double-double, if the accurate DoubleDouble doesn't handle the -// operation. It's equivalent to having an IEEE number with consecutive 106 -// bits of mantissa and 11 bits of exponent. -// -// It's not equivalent to IBM double-double. For example, a legit IBM -// double-double, 1 + epsilon: -// -// 1 + epsilon = 1 + (1 >> 1076) -// -// is not representable by a consecutive 106 bits of mantissa. -// -// Currently, these semantics are used in the following way: -// -// DoubleDouble -> (Double, Double) -> -// DoubleDouble's Fallback -> IEEE operations -// -// FIXME: Implement all operations in DoubleDouble, and delete these -// semantics. -// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds. -pub struct FallbackS(#[allow(unused)] F); -type Fallback = ieee::IeeeFloat>; -impl ieee::Semantics for FallbackS { - // Forbid any conversion to/from bits. - const BITS: usize = 0; - const PRECISION: usize = F::PRECISION * 2; - const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt; - const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt; -} - -// Convert number to F. To avoid spurious underflows, we re- -// normalize against the F exponent range first, and only *then* -// truncate the mantissa. The result of that second conversion -// may be inexact, but should never underflow. -// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds. -pub struct FallbackExtendedS(#[allow(unused)] F); -type FallbackExtended = ieee::IeeeFloat>; -impl ieee::Semantics for FallbackExtendedS { - // Forbid any conversion to/from bits. - const BITS: usize = 0; - const PRECISION: usize = Fallback::::PRECISION; - const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt; -} - -impl From> for DoubleFloat -where - F: FloatConvert>, - FallbackExtended: FloatConvert, -{ - fn from(x: Fallback) -> Self { - let mut status; - let mut loses_info = false; - - let extended: FallbackExtended = unpack!(status=, x.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - let a = unpack!(status=, extended.convert(&mut loses_info)); - assert_eq!(status - Status::INEXACT, Status::OK); - - // If conversion was exact or resulted in a special case, we're done; - // just set the second double to zero. Otherwise, re-convert back to - // the extended format and compute the difference. This now should - // convert exactly to double. - let b = if a.is_finite_non_zero() && loses_info { - let u: FallbackExtended = unpack!(status=, a.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - let v = unpack!(status=, extended - u); - assert_eq!(status, Status::OK); - let v = unpack!(status=, v.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - v - } else { - F::ZERO - }; - - DoubleFloat(a, b) - } -} - -impl> From> for Fallback { - fn from(DoubleFloat(a, b): DoubleFloat) -> Self { - let mut status; - let mut loses_info = false; - - // Get the first F and convert to our format. - let a = unpack!(status=, a.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - // Unless we have a special case, add in second F. - if a.is_finite_non_zero() { - let b = unpack!(status=, b.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - (a + b).value - } else { - a - } - } -} - -float_common_impls!(DoubleFloat); - -impl Neg for DoubleFloat { - type Output = Self; - fn neg(self) -> Self { - if self.1.is_finite_non_zero() { - DoubleFloat(-self.0, -self.1) - } else { - DoubleFloat(-self.0, self.1) - } - } -} - -impl>> fmt::Display for DoubleFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&Fallback::from(*self), f) - } -} - -impl>> Float for DoubleFloat -where - Self: From>, -{ - const BITS: usize = F::BITS * 2; - const PRECISION: usize = Fallback::::PRECISION; - const MAX_EXP: ExpInt = Fallback::::MAX_EXP; - const MIN_EXP: ExpInt = Fallback::::MIN_EXP; - - const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO); - - const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO); - - // FIXME(eddyb) remove when qnan becomes const fn. - const NAN: Self = DoubleFloat(F::NAN, F::ZERO); - - fn qnan(payload: Option) -> Self { - DoubleFloat(F::qnan(payload), F::ZERO) - } - - fn snan(payload: Option) -> Self { - DoubleFloat(F::snan(payload), F::ZERO) - } - - fn largest() -> Self { - let status; - let mut r = DoubleFloat(F::largest(), F::largest()); - r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1)); - r.1 = unpack!(status=, r.1.next_down()); - assert_eq!(status, Status::OK); - r - } - - const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO); - - fn smallest_normalized() -> Self { - DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO) - } - - // Implement addition, subtraction, multiplication and division based on: - // "Software for Doubled-Precision Floating-Point Computations", - // by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283. - - fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd { - match (self.category(), rhs.category()) { - (Category::Infinity, Category::Infinity) => { - if self.is_negative() != rhs.is_negative() { - Status::INVALID_OP.and(Self::NAN.copy_sign(self)) - } else { - Status::OK.and(self) - } - } - - (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => { - Status::OK.and(self) - } - - (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs), - - (Category::Normal, Category::Normal) => { - let mut status = Status::OK; - let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1); - let mut z = a; - z = unpack!(status|=, z.add_r(c, round)); - if !z.is_finite() { - if !z.is_infinite() { - return status.and(DoubleFloat(z, F::ZERO)); - } - status = Status::OK; - let a_cmp_c = a.cmp_abs_normal(c); - z = cc; - z = unpack!(status|=, z.add_r(aa, round)); - if a_cmp_c == Ordering::Greater { - // z = cc + aa + c + a; - z = unpack!(status|=, z.add_r(c, round)); - z = unpack!(status|=, z.add_r(a, round)); - } else { - // z = cc + aa + a + c; - z = unpack!(status|=, z.add_r(a, round)); - z = unpack!(status|=, z.add_r(c, round)); - } - if !z.is_finite() { - return status.and(DoubleFloat(z, F::ZERO)); - } - self.0 = z; - let mut zz = aa; - zz = unpack!(status|=, zz.add_r(cc, round)); - if a_cmp_c == Ordering::Greater { - // self.1 = a - z + c + zz; - self.1 = a; - self.1 = unpack!(status|=, self.1.sub_r(z, round)); - self.1 = unpack!(status|=, self.1.add_r(c, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } else { - // self.1 = c - z + a + zz; - self.1 = c; - self.1 = unpack!(status|=, self.1.sub_r(z, round)); - self.1 = unpack!(status|=, self.1.add_r(a, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } - } else { - // q = a - z; - let mut q = a; - q = unpack!(status|=, q.sub_r(z, round)); - - // zz = q + c + (a - (q + z)) + aa + cc; - // Compute a - (q + z) as -((q + z) - a) to avoid temporary copies. - let mut zz = q; - zz = unpack!(status|=, zz.add_r(c, round)); - q = unpack!(status|=, q.add_r(z, round)); - q = unpack!(status|=, q.sub_r(a, round)); - q = -q; - zz = unpack!(status|=, zz.add_r(q, round)); - zz = unpack!(status|=, zz.add_r(aa, round)); - zz = unpack!(status|=, zz.add_r(cc, round)); - if zz.is_zero() && !zz.is_negative() { - return Status::OK.and(DoubleFloat(z, F::ZERO)); - } - self.0 = z; - self.0 = unpack!(status|=, self.0.add_r(zz, round)); - if !self.0.is_finite() { - self.1 = F::ZERO; - return status.and(self); - } - self.1 = z; - self.1 = unpack!(status|=, self.1.sub_r(self.0, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } - status.and(self) - } - } - } - - fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd { - // Interesting observation: For special categories, finding the lowest - // common ancestor of the following layered graph gives the correct - // return category: - // - // NaN - // / \ - // Zero Inf - // \ / - // Normal - // - // e.g., NaN * NaN = NaN - // Zero * Inf = NaN - // Normal * Zero = Zero - // Normal * Inf = Inf - match (self.category(), rhs.category()) { - (Category::NaN, _) => Status::OK.and(self), - - (_, Category::NaN) => Status::OK.and(rhs), - - (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => { - Status::OK.and(Self::NAN) - } - - (Category::Zero | Category::Infinity, _) => Status::OK.and(self), - - (_, Category::Zero | Category::Infinity) => Status::OK.and(rhs), - - (Category::Normal, Category::Normal) => { - let mut status = Status::OK; - let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1); - // t = a * c - let mut t = a; - t = unpack!(status|=, t.mul_r(c, round)); - if !t.is_finite_non_zero() { - return status.and(DoubleFloat(t, F::ZERO)); - } - - // tau = fmsub(a, c, t), that is -fmadd(-a, c, t). - let mut tau = a; - tau = unpack!(status|=, tau.mul_add_r(c, -t, round)); - // v = a * d - let mut v = a; - v = unpack!(status|=, v.mul_r(d, round)); - // w = b * c - let mut w = b; - w = unpack!(status|=, w.mul_r(c, round)); - v = unpack!(status|=, v.add_r(w, round)); - // tau += v + w - tau = unpack!(status|=, tau.add_r(v, round)); - // u = t + tau - let mut u = t; - u = unpack!(status|=, u.add_r(tau, round)); - - self.0 = u; - if !u.is_finite() { - self.1 = F::ZERO; - } else { - // self.1 = (t - u) + tau - t = unpack!(status|=, t.sub_r(u, round)); - t = unpack!(status|=, t.add_r(tau, round)); - self.1 = t; - } - status.and(self) - } - } - } - - fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd { - Fallback::from(self) - .mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round) - .map(Self::from) - } - - fn div_r(self, rhs: Self, round: Round) -> StatusAnd { - Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from) - } - - fn c_fmod(self, rhs: Self) -> StatusAnd { - Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from) - } - - fn round_to_integral(self, round: Round) -> StatusAnd { - Fallback::from(self).round_to_integral(round).map(Self::from) - } - - fn next_up(self) -> StatusAnd { - Fallback::from(self).next_up().map(Self::from) - } - - fn from_bits(input: u128) -> Self { - let (a, b) = (input, input >> F::BITS); - DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1))) - } - - fn from_u128_r(input: u128, round: Round) -> StatusAnd { - Fallback::from_u128_r(input, round).map(Self::from) - } - - fn from_str_r(s: &str, round: Round) -> Result, ParseError> { - Fallback::from_str_r(s, round).map(|r| r.map(Self::from)) - } - - fn to_bits(self) -> u128 { - self.0.to_bits() | (self.1.to_bits() << F::BITS) - } - - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - Fallback::from(self).to_u128_r(width, round, is_exact) - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering { - self.0.cmp_abs_normal(rhs.0).then_with(|| { - let result = self.1.cmp_abs_normal(rhs.1); - if result != Ordering::Equal { - let against = self.0.is_negative() ^ self.1.is_negative(); - let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative(); - (!against) - .cmp(&!rhs_against) - .then_with(|| if against { result.reverse() } else { result }) - } else { - result - } - }) - } - - fn bitwise_eq(self, rhs: Self) -> bool { - self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1) - } - - fn is_negative(self) -> bool { - self.0.is_negative() - } - - fn is_denormal(self) -> bool { - self.category() == Category::Normal - && (self.0.is_denormal() || self.0.is_denormal() || - // (double)(Hi + Lo) == Hi defines a normal number. - !(self.0 + self.1).value.bitwise_eq(self.0)) - } - - fn is_signaling(self) -> bool { - self.0.is_signaling() - } - - fn category(self) -> Category { - self.0.category() - } - - fn get_exact_inverse(self) -> Option { - Fallback::from(self).get_exact_inverse().map(Self::from) - } - - fn ilogb(self) -> ExpInt { - self.0.ilogb() - } - - fn scalbn_r(self, exp: ExpInt, round: Round) -> Self { - DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round)) - } - - fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self { - let a = self.0.frexp_r(exp, round); - let mut b = self.1; - if self.category() == Category::Normal { - b = b.scalbn_r(-*exp, round); - } - DoubleFloat(a, b) - } -} diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs deleted file mode 100644 index f8fac0c2358..00000000000 --- a/compiler/rustc_apfloat/tests/ieee.rs +++ /dev/null @@ -1,3301 +0,0 @@ -// ignore-tidy-filelength - -use rustc_apfloat::ieee::{Double, Half, Quad, Single, X87DoubleExtended}; -use rustc_apfloat::unpack; -use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO}; -use rustc_apfloat::{Float, FloatConvert, ParseError, Round, Status}; - -trait SingleExt { - fn from_f32(input: f32) -> Self; - fn to_f32(self) -> f32; -} - -impl SingleExt for Single { - fn from_f32(input: f32) -> Self { - Self::from_bits(input.to_bits() as u128) - } - - fn to_f32(self) -> f32 { - f32::from_bits(self.to_bits() as u32) - } -} - -trait DoubleExt { - fn from_f64(input: f64) -> Self; - fn to_f64(self) -> f64; -} - -impl DoubleExt for Double { - fn from_f64(input: f64) -> Self { - Self::from_bits(input.to_bits() as u128) - } - - fn to_f64(self) -> f64 { - f64::from_bits(self.to_bits() as u64) - } -} - -#[test] -fn is_signaling() { - // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads. - let payload = 4; - assert!(!Single::qnan(None).is_signaling()); - assert!(!(-Single::qnan(None)).is_signaling()); - assert!(!Single::qnan(Some(payload)).is_signaling()); - assert!(!(-Single::qnan(Some(payload))).is_signaling()); - assert!(Single::snan(None).is_signaling()); - assert!((-Single::snan(None)).is_signaling()); - assert!(Single::snan(Some(payload)).is_signaling()); - assert!((-Single::snan(Some(payload))).is_signaling()); -} - -#[test] -fn next() { - // 1. Test Special Cases Values. - // - // Test all special values for nextUp and nextDown perscribed by IEEE-754R - // 2008. These are: - // 1. +inf - // 2. -inf - // 3. largest - // 4. -largest - // 5. smallest - // 6. -smallest - // 7. qNaN - // 8. sNaN - // 9. +0 - // 10. -0 - - let mut status; - - // nextUp(+inf) = +inf. - let test = unpack!(status=, Quad::INFINITY.next_up()); - let expected = Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+inf) = -nextUp(-inf) = -(-largest) = largest - let test = unpack!(status=, Quad::INFINITY.next_down()); - let expected = Quad::largest(); - assert_eq!(status, Status::OK); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-inf) = -largest - let test = unpack!(status=, (-Quad::INFINITY).next_up()); - let expected = -Quad::largest(); - assert_eq!(status, Status::OK); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf. - let test = unpack!(status=, (-Quad::INFINITY).next_down()); - let expected = -Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(largest) = +inf - let test = unpack!(status=, Quad::largest().next_up()); - let expected = Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && !test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(largest) = -nextUp(-largest) - // = -(-largest + inc) - // = largest - inc. - let test = unpack!(status=, Quad::largest().next_down()); - let expected = "0x1.fffffffffffffffffffffffffffep+16383".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_infinite() && !test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-largest) = -largest + inc. - let test = unpack!(status=, (-Quad::largest()).next_up()); - let expected = "-0x1.fffffffffffffffffffffffffffep+16383".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-largest) = -nextUp(largest) = -(inf) = -inf. - let test = unpack!(status=, (-Quad::largest()).next_down()); - let expected = -Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(smallest) = smallest + inc. - let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x0.0000000000000000000000000002p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(smallest) = -nextUp(-smallest) = -(-0) = +0. - let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = Quad::ZERO; - assert_eq!(status, Status::OK); - assert!(test.is_pos_zero()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-smallest) = -0. - let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = -Quad::ZERO; - assert_eq!(status, Status::OK); - assert!(test.is_neg_zero()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-smallest) = -nextUp(smallest) = -smallest - inc. - let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x0.0000000000000000000000000002p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(qNaN) = qNaN - let test = unpack!(status=, Quad::qnan(None).next_up()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(qNaN) = qNaN - let test = unpack!(status=, Quad::qnan(None).next_down()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(sNaN) = qNaN - let test = unpack!(status=, Quad::snan(None).next_up()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::INVALID_OP); - assert!(test.bitwise_eq(expected)); - - // nextDown(sNaN) = qNaN - let test = unpack!(status=, Quad::snan(None).next_down()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::INVALID_OP); - assert!(test.bitwise_eq(expected)); - - // nextUp(+0) = +smallest - let test = unpack!(status=, Quad::ZERO.next_up()); - let expected = Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(+0) = -nextUp(-0) = -smallest - let test = unpack!(status=, Quad::ZERO.next_down()); - let expected = -Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(-0) = +smallest - let test = unpack!(status=, (-Quad::ZERO).next_up()); - let expected = Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-0) = -nextUp(0) = -smallest - let test = unpack!(status=, (-Quad::ZERO).next_down()); - let expected = -Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 2. Binade Boundary Tests. - - // 2a. Test denormal <-> normal binade boundaries. - // * nextUp(+Largest Denormal) -> +Smallest Normal. - // * nextDown(-Largest Denormal) -> -Smallest Normal. - // * nextUp(-Smallest Normal) -> -Largest Denormal. - // * nextDown(+Smallest Normal) -> +Largest Denormal. - - // nextUp(+Largest Denormal) -> +Smallest Normal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000000p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Largest Denormal) -> -Smallest Normal. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000000p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Smallest Normal) -> -Largest Denormal. - let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Smallest Normal) -> +Largest Denormal. - let test = unpack!(status=, "+0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "+0x0.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // 2b. Test normal <-> normal binade boundaries. - // * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. - // * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. - // * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. - // * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. - - // nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. - let test = unpack!(status=, "-0x1p+1".parse::().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp+0".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. - let test = unpack!(status=, "0x1p+1".parse::().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp+0".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp+0" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1p+1".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp+0" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1p+1".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 2c. Test using next at binade boundaries with a direction away from the - // binade boundary. Away from denormal <-> normal boundaries. - // - // This is to make sure that even though we are at a binade boundary, since - // we are rounding away, we do not trigger the binade boundary code. Thus we - // test: - // * nextUp(-Largest Denormal) -> -Largest Denormal + inc. - // * nextDown(+Largest Denormal) -> +Largest Denormal - inc. - // * nextUp(+Smallest Normal) -> +Smallest Normal + inc. - // * nextDown(-Smallest Normal) -> -Smallest Normal - inc. - - // nextUp(-Largest Denormal) -> -Largest Denormal + inc. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.fffffffffffffffffffffffffffep-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Largest Denormal) -> +Largest Denormal - inc. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "0x0.fffffffffffffffffffffffffffep-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Smallest Normal) -> +Smallest Normal + inc. - let test = unpack!(status=, "0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000001p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Smallest Normal) -> -Smallest Normal - inc. - let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000001p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // 2d. Test values which cause our exponent to go to min exponent. This - // is to ensure that guards in the code to check for min exponent - // trigger properly. - // * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 - // * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> - // -0x1p-16381 - // * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382 - // * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382 - - // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 - let test = unpack!(status=, "-0x1p-16381".parse::().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> - // -0x1p-16381 - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1p-16381".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381 - let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1p-16381".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382 - let test = unpack!(status=, "0x1p-16381".parse::().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 3. Now we test both denormal/normal computation which will not cause us - // to go across binade boundaries. Specifically we test: - // * nextUp(+Denormal) -> +Denormal. - // * nextDown(+Denormal) -> +Denormal. - // * nextUp(-Denormal) -> -Denormal. - // * nextDown(-Denormal) -> -Denormal. - // * nextUp(+Normal) -> +Normal. - // * nextDown(+Normal) -> +Normal. - // * nextUp(-Normal) -> -Normal. - // * nextDown(-Normal) -> -Normal. - - // nextUp(+Denormal) -> +Denormal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x0.ffffffffffffffffffffffff000dp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Denormal) -> +Denormal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "0x0.ffffffffffffffffffffffff000bp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Denormal) -> -Denormal. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffff000bp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Denormal) -> -Denormal - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x0.ffffffffffffffffffffffff000dp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Normal) -> +Normal. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.ffffffffffffffffffffffff000dp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Normal) -> +Normal. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_down()); - let expected = "0x1.ffffffffffffffffffffffff000bp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Normal) -> -Normal. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x1.ffffffffffffffffffffffff000bp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Normal) -> -Normal. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.ffffffffffffffffffffffff000dp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn fma() { - { - let mut f1 = Single::from_f32(14.5); - let f2 = Single::from_f32(-14.5); - let f3 = Single::from_f32(225.0); - f1 = f1.mul_add(f2, f3).value; - assert_eq!(14.75, f1.to_f32()); - } - - { - let val2 = Single::from_f32(2.0); - let mut f1 = Single::from_f32(1.17549435e-38); - let mut f2 = Single::from_f32(1.17549435e-38); - f1 /= val2; - f2 /= val2; - let f3 = Single::from_f32(12.0); - f1 = f1.mul_add(f2, f3).value; - assert_eq!(12.0, f1.to_f32()); - } - - // Test for correct zero sign when answer is exactly zero. - // fma(1.0, -1.0, 1.0) -> +ve 0. - { - let mut f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(-1.0); - let f3 = Double::from_f64(1.0); - f1 = f1.mul_add(f2, f3).value; - assert!(!f1.is_negative() && f1.is_zero()); - } - - // Test for correct zero sign when answer is exactly zero and rounding towards - // negative. - // fma(1.0, -1.0, 1.0) -> +ve 0. - { - let mut f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(-1.0); - let f3 = Double::from_f64(1.0); - f1 = f1.mul_add_r(f2, f3, Round::TowardNegative).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test for correct (in this case -ve) sign when adding like signed zeros. - // Test fma(0.0, -0.0, -0.0) -> -ve 0. - { - let mut f1 = Double::from_f64(0.0); - let f2 = Double::from_f64(-0.0); - let f3 = Double::from_f64(-0.0); - f1 = f1.mul_add(f2, f3).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test -ve sign preservation when small negative results underflow. - { - let mut f1 = "-0x1p-1074".parse::().unwrap(); - let f2 = "+0x1p-1074".parse::().unwrap(); - let f3 = Double::from_f64(0.0); - f1 = f1.mul_add(f2, f3).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test x87 extended precision case from https://llvm.org/PR20728. - { - let mut m1 = X87DoubleExtended::from_u128(1).value; - let m2 = X87DoubleExtended::from_u128(1).value; - let a = X87DoubleExtended::from_u128(3).value; - - let mut loses_info = false; - m1 = m1.mul_add(m2, a).value; - let r: Single = m1.convert(&mut loses_info).value; - assert!(!loses_info); - assert_eq!(4.0, r.to_f32()); - } -} - -#[test] -fn issue_69532() { - let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128); - let mut loses_info = false; - let sta = f.convert(&mut loses_info); - let r: Single = sta.value; - assert!(loses_info); - assert!(r.is_nan()); - assert_eq!(sta.status, Status::INVALID_OP); -} - -#[test] -fn min_num() { - let f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(2.0); - let nan = Double::NAN; - - assert_eq!(1.0, f1.min(f2).to_f64()); - assert_eq!(1.0, f2.min(f1).to_f64()); - assert_eq!(1.0, f1.min(nan).to_f64()); - assert_eq!(1.0, nan.min(f1).to_f64()); -} - -#[test] -fn max_num() { - let f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(2.0); - let nan = Double::NAN; - - assert_eq!(2.0, f1.max(f2).to_f64()); - assert_eq!(2.0, f2.max(f1).to_f64()); - assert_eq!(1.0, f1.max(nan).to_f64()); - assert_eq!(1.0, nan.max(f1).to_f64()); -} - -#[test] -fn denormal() { - // Test single precision - { - assert!(!Single::from_f32(0.0).is_denormal()); - - let mut t = "1.17549435082228750797e-38".parse::().unwrap(); - assert!(!t.is_denormal()); - - let val2 = Single::from_f32(2.0e0); - t /= val2; - assert!(t.is_denormal()); - } - - // Test double precision - { - assert!(!Double::from_f64(0.0).is_denormal()); - - let mut t = "2.22507385850720138309e-308".parse::().unwrap(); - assert!(!t.is_denormal()); - - let val2 = Double::from_f64(2.0e0); - t /= val2; - assert!(t.is_denormal()); - } - - // Test Intel double-ext - { - assert!(!X87DoubleExtended::from_u128(0).value.is_denormal()); - - let mut t = "3.36210314311209350626e-4932".parse::().unwrap(); - assert!(!t.is_denormal()); - - t /= X87DoubleExtended::from_u128(2).value; - assert!(t.is_denormal()); - } - - // Test quadruple precision - { - assert!(!Quad::from_u128(0).value.is_denormal()); - - let mut t = "3.36210314311209350626267781732175260e-4932".parse::().unwrap(); - assert!(!t.is_denormal()); - - t /= Quad::from_u128(2).value; - assert!(t.is_denormal()); - } -} - -#[test] -fn decimal_strings_without_null_terminators() { - // Make sure that we can parse strings without null terminators. - // rdar://14323230. - let val = "0.00"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.01"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.09"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.095"[..4].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.09); - let val = "0.00e+3"[..7].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.00); - let val = "0e+3"[..4].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.00); -} - -#[test] -fn from_zero_decimal_string() { - assert_eq!(0.0, "0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "00000.".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+00000.".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-00000.".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".00000".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.00000".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.00000".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0000.00000".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0000.00000".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0000.00000".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_single_exponent_string() { - assert_eq!(0.0, "0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+000.0000e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-000.0000e+1".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_large_exponent_string() { - assert_eq!(0.0, "0e1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "000.0000e-1234".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_hexadecimal_string() { - assert_eq!(0.0, "0x0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x00000.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0p1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x00000.p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1234".parse::().unwrap().to_f64()); -} - -#[test] -fn from_decimal_string() { - assert_eq!(1.0, "1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::().unwrap().to_f64()); - assert_eq!(0.5, ".5".parse::().unwrap().to_f64()); - assert_eq!(1.0, "1.0".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-2".parse::().unwrap().to_f64()); - assert_eq!(-4.0, "-4.".parse::().unwrap().to_f64()); - assert_eq!(-0.5, "-.5".parse::().unwrap().to_f64()); - assert_eq!(-1.5, "-1.5".parse::().unwrap().to_f64()); - assert_eq!(1.25e12, "1.25e12".parse::().unwrap().to_f64()); - assert_eq!(1.25e+12, "1.25e+12".parse::().unwrap().to_f64()); - assert_eq!(1.25e-12, "1.25e-12".parse::().unwrap().to_f64()); - assert_eq!(1024.0, "1024.".parse::().unwrap().to_f64()); - assert_eq!(1024.05, "1024.05000".parse::().unwrap().to_f64()); - assert_eq!(0.05, ".05000".parse::().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::().unwrap().to_f64()); - assert_eq!(2.0e2, "2.e2".parse::().unwrap().to_f64()); - assert_eq!(2.0e+2, "2.e+2".parse::().unwrap().to_f64()); - assert_eq!(2.0e-2, "2.e-2".parse::().unwrap().to_f64()); - assert_eq!(2.05e2, "002.05000e2".parse::().unwrap().to_f64()); - assert_eq!(2.05e+2, "002.05000e+2".parse::().unwrap().to_f64()); - assert_eq!(2.05e-2, "002.05000e-2".parse::().unwrap().to_f64()); - assert_eq!(2.05e12, "002.05000e12".parse::().unwrap().to_f64()); - assert_eq!(2.05e+12, "002.05000e+12".parse::().unwrap().to_f64()); - assert_eq!(2.05e-12, "002.05000e-12".parse::().unwrap().to_f64()); - - // These are "carefully selected" to overflow the fast log-base - // calculations in the implementation. - assert!("99e99999".parse::().unwrap().is_infinite()); - assert!("-99e99999".parse::().unwrap().is_infinite()); - assert!("1e-99999".parse::().unwrap().is_pos_zero()); - assert!("-1e-99999".parse::().unwrap().is_neg_zero()); - - assert_eq!(2.71828, "2.71828".parse::().unwrap().to_f64()); -} - -#[test] -fn from_hexadecimal_string() { - assert_eq!(1.0, "0x1p0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p0".parse::().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p+0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p+0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p+0".parse::().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p-0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p-0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p-0".parse::().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p1".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p1".parse::().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p+1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p+1".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.5, "0x1p-1".parse::().unwrap().to_f64()); - assert_eq!(0.5, "+0x1p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.5, "-0x1p-1".parse::().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p1".parse::().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p1".parse::().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p1".parse::().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p+1".parse::().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p+1".parse::().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.75, "0x1.8p-1".parse::().unwrap().to_f64()); - assert_eq!(0.75, "+0x1.8p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.75, "-0x1.8p-1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p+1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p+1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p+1".parse::().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000.000p-1".parse::().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000.000p-1".parse::().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000.000p-1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p+1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p+1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p+1".parse::().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000p-1".parse::().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000p-1".parse::().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000p-1".parse::().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p10".parse::().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p10".parse::().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p10".parse::().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p+10".parse::().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p+10".parse::().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p+10".parse::().unwrap().to_f64()); - - assert_eq!(0.015625, "0x10p-10".parse::().unwrap().to_f64()); - assert_eq!(0.015625, "+0x10p-10".parse::().unwrap().to_f64()); - assert_eq!(-0.015625, "-0x10p-10".parse::().unwrap().to_f64()); - - assert_eq!(1.0625, "0x1.1p0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "0x1p0".parse::().unwrap().to_f64()); - - assert_eq!( - "0x1p-150".parse::().unwrap().to_f64(), - "+0x800000000000000001.p-221".parse::().unwrap().to_f64() - ); - assert_eq!( - 2251799813685248.5, - "0x80000000000004000000.010p-28".parse::().unwrap().to_f64() - ); -} - -#[test] -fn to_string() { - let to_string = |d: f64, precision: usize, width: usize| { - let x = Double::from_f64(d); - if precision == 0 { - format!("{:1$}", x, width) - } else { - format!("{:2$.1$}", x, precision, width) - } - }; - assert_eq!("10", to_string(10.0, 6, 3)); - assert_eq!("1.0E+1", to_string(10.0, 6, 0)); - assert_eq!("10100", to_string(1.01E+4, 5, 2)); - assert_eq!("1.01E+4", to_string(1.01E+4, 4, 2)); - assert_eq!("1.01E+4", to_string(1.01E+4, 5, 1)); - assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); - assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); - assert_eq!("1.01E-2", to_string(1.01E-2, 5, 1)); - assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); - assert_eq!("4.9406564584124654E-324", to_string(4.9406564584124654e-324, 0, 3)); - assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); - assert_eq!("8.7318340000000001E+2", to_string(873.1834, 0, 0)); - assert_eq!("1.7976931348623157E+308", to_string(1.7976931348623157E+308, 0, 0)); - - let to_string = |d: f64, precision: usize, width: usize| { - let x = Double::from_f64(d); - if precision == 0 { - format!("{:#1$}", x, width) - } else { - format!("{:#2$.1$}", x, precision, width) - } - }; - assert_eq!("10", to_string(10.0, 6, 3)); - assert_eq!("1.000000e+01", to_string(10.0, 6, 0)); - assert_eq!("10100", to_string(1.01E+4, 5, 2)); - assert_eq!("1.0100e+04", to_string(1.01E+4, 4, 2)); - assert_eq!("1.01000e+04", to_string(1.01E+4, 5, 1)); - assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); - assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); - assert_eq!("1.01000e-02", to_string(1.01E-2, 5, 1)); - assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); - assert_eq!("4.94065645841246540e-324", to_string(4.9406564584124654e-324, 0, 3)); - assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); - assert_eq!("8.73183400000000010e+02", to_string(873.1834, 0, 0)); - assert_eq!("1.79769313486231570e+308", to_string(1.7976931348623157E+308, 0, 0)); -} - -#[test] -fn to_integer() { - let mut is_exact = false; - - assert_eq!( - Status::OK.and(10), - "10".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(0), - "-10".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(31), - "32".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INEXACT.and(7), - "7.9".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::OK.and(-10), - "-10".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(-16), - "-17".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(15), - "16".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); -} - -#[test] -fn nan() { - fn nanbits(signaling: bool, negative: bool, fill: u128) -> u128 { - let x = if signaling { T::snan(Some(fill)) } else { T::qnan(Some(fill)) }; - if negative { (-x).to_bits() } else { x.to_bits() } - } - - assert_eq!(0x7fc00000, nanbits::(false, false, 0)); - assert_eq!(0xffc00000, nanbits::(false, true, 0)); - assert_eq!(0x7fc0ae72, nanbits::(false, false, 0xae72)); - assert_eq!(0x7fffae72, nanbits::(false, false, 0xffffae72)); - assert_eq!(0x7fa00000, nanbits::(true, false, 0)); - assert_eq!(0xffa00000, nanbits::(true, true, 0)); - assert_eq!(0x7f80ae72, nanbits::(true, false, 0xae72)); - assert_eq!(0x7fbfae72, nanbits::(true, false, 0xffffae72)); - - assert_eq!(0x7ff8000000000000, nanbits::(false, false, 0)); - assert_eq!(0xfff8000000000000, nanbits::(false, true, 0)); - assert_eq!(0x7ff800000000ae72, nanbits::(false, false, 0xae72)); - assert_eq!(0x7fffffffffffae72, nanbits::(false, false, 0xffffffffffffae72)); - assert_eq!(0x7ff4000000000000, nanbits::(true, false, 0)); - assert_eq!(0xfff4000000000000, nanbits::(true, true, 0)); - assert_eq!(0x7ff000000000ae72, nanbits::(true, false, 0xae72)); - assert_eq!(0x7ff7ffffffffae72, nanbits::(true, false, 0xffffffffffffae72)); -} - -#[test] -fn string_decimal_death() { - assert_eq!("".parse::(), Err(ParseError("Invalid string length"))); - assert_eq!("+".parse::(), Err(ParseError("String has no digits"))); - assert_eq!("-".parse::(), Err(ParseError("String has no digits"))); - - assert_eq!("\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02e1".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1e\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\02".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("1.0f".parse::(), Err(ParseError("Invalid character in significand"))); - - assert_eq!("..".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("..0".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("1.0.0".parse::(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_decimal_significand_death() { - assert_eq!(".".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e1".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e1".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e".parse::(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_decimal_exponent_death() { - assert_eq!("1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-.1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1e-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.0e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e-".parse::(), Err(ParseError("Exponent has no digits"))); -} - -#[test] -fn string_hexadecimal_death() { - assert_eq!("0x".parse::(), Err(ParseError("Invalid string"))); - assert_eq!("+0x".parse::(), Err(ParseError("Invalid string"))); - assert_eq!("-0x".parse::(), Err(ParseError("Invalid string"))); - - assert_eq!("0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02p1".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1p\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\02".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x1p0f".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x..p1".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x..0p1".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x1.0.0p1".parse::(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_hexadecimal_significand_death() { - assert_eq!("0x.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp+".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp-".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p-".parse::(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_hexadecimal_exponent_death() { - assert_eq!("0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); -} - -#[test] -fn exact_inverse() { - // Trivial operation. - assert!(Double::from_f64(2.0).get_exact_inverse().unwrap().bitwise_eq(Double::from_f64(0.5))); - assert!(Single::from_f32(2.0).get_exact_inverse().unwrap().bitwise_eq(Single::from_f32(0.5))); - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); - - // FLT_MIN - assert!( - Single::from_f32(1.17549435e-38) - .get_exact_inverse() - .unwrap() - .bitwise_eq(Single::from_f32(8.5070592e+37)) - ); - - // Large float, inverse is a denormal. - assert!(Single::from_f32(1.7014118e38).get_exact_inverse().is_none()); - // Zero - assert!(Double::from_f64(0.0).get_exact_inverse().is_none()); - // Denormalized float - assert!(Single::from_f32(1.40129846e-45).get_exact_inverse().is_none()); -} - -#[test] -fn round_to_integral() { - let t = Double::from_f64(-0.5); - assert_eq!(-0.0, t.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(-1.0, t.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(-0.0, t.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(-0.0, t.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let s = Double::from_f64(3.14); - assert_eq!(3.0, s.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(3.0, s.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(4.0, s.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(3.0, s.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let r = Double::largest(); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let p = Double::ZERO.round_to_integral(Round::TowardZero).value; - assert_eq!(0.0, p.to_f64()); - let p = (-Double::ZERO).round_to_integral(Round::TowardZero).value; - assert_eq!(-0.0, p.to_f64()); - let p = Double::NAN.round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_nan()); - let p = Double::INFINITY.round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_infinite() && p.to_f64() > 0.0); - let p = (-Double::INFINITY).round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_infinite() && p.to_f64() < 0.0); -} - -#[test] -fn is_integer() { - let t = Double::from_f64(-0.0); - assert!(t.is_integer()); - let t = Double::from_f64(3.14159); - assert!(!t.is_integer()); - let t = Double::NAN; - assert!(!t.is_integer()); - let t = Double::INFINITY; - assert!(!t.is_integer()); - let t = -Double::INFINITY; - assert!(!t.is_integer()); - let t = Double::largest(); - assert!(t.is_integer()); -} - -#[test] -fn largest() { - assert_eq!(3.402823466e+38, Single::largest().to_f32()); - assert_eq!(1.7976931348623158e+308, Double::largest().to_f64()); -} - -#[test] -fn smallest() { - let test = Single::SMALLEST; - let expected = "0x0.000002p-126".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Single::SMALLEST; - let expected = "-0x0.000002p-126".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = Quad::SMALLEST; - let expected = "0x0.0000000000000000000000000001p-16382".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Quad::SMALLEST; - let expected = "-0x0.0000000000000000000000000001p-16382".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn smallest_normalized() { - let test = Single::smallest_normalized(); - let expected = "0x1p-126".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Single::smallest_normalized(); - let expected = "-0x1p-126".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = Quad::smallest_normalized(); - let expected = "0x1p-16382".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Quad::smallest_normalized(); - let expected = "-0x1p-16382".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn zero() { - assert_eq!(0.0, Single::from_f32(0.0).to_f32()); - assert_eq!(-0.0, Single::from_f32(-0.0).to_f32()); - assert!(Single::from_f32(-0.0).is_negative()); - - assert_eq!(0.0, Double::from_f64(0.0).to_f64()); - assert_eq!(-0.0, Double::from_f64(-0.0).to_f64()); - assert!(Double::from_f64(-0.0).is_negative()); - - fn test(sign: bool, bits: u128) { - let test = if sign { -T::ZERO } else { T::ZERO }; - let pattern = if sign { "-0x0p+0" } else { "0x0p+0" }; - let expected = pattern.parse::().unwrap(); - assert!(test.is_zero()); - assert_eq!(sign, test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(bits, test.to_bits()); - } - test::(false, 0); - test::(true, 0x8000); - test::(false, 0); - test::(true, 0x80000000); - test::(false, 0); - test::(true, 0x8000000000000000); - test::(false, 0); - test::(true, 0x8000000000000000_0000000000000000); - test::(false, 0); - test::(true, 0x8000_0000000000000000); -} - -#[test] -fn copy_sign() { - assert!( - Double::from_f64(-42.0) - .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(-1.0),),) - ); - assert!( - Double::from_f64(42.0) - .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(1.0),),) - ); - assert!( - Double::from_f64(-42.0) - .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(-1.0),),) - ); - assert!( - Double::from_f64(42.0) - .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(1.0),),) - ); -} - -#[test] -fn convert() { - let mut loses_info = false; - let test = "1.0".parse::().unwrap(); - let test: Single = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f32()); - assert!(!loses_info); - - let mut test = "0x1p-53".parse::().unwrap(); - let one = "1.0".parse::().unwrap(); - test += one; - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f64()); - assert!(loses_info); - - let mut test = "0x1p-53".parse::().unwrap(); - let one = "1.0".parse::().unwrap(); - test += one; - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f64()); - assert!(loses_info); - - let test = "0xf.fffffffp+28".parse::().unwrap(); - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(4294967295.0, test.to_f64()); - assert!(!loses_info); - - let test = Single::qnan(None); - let x87_qnan = X87DoubleExtended::qnan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_qnan)); - assert!(!loses_info); - - let test = Single::snan(None); - let sta = test.convert(&mut loses_info); - let test: X87DoubleExtended = sta.value; - assert!(test.is_nan()); - assert!(!test.is_signaling()); - assert!(!loses_info); - assert_eq!(sta.status, Status::INVALID_OP); - - let test = X87DoubleExtended::qnan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_qnan)); - assert!(!loses_info); - - let test = X87DoubleExtended::snan(None); - let sta = test.convert(&mut loses_info); - let test: X87DoubleExtended = sta.value; - assert!(test.is_nan()); - assert!(!test.is_signaling()); - assert!(!loses_info); - assert_eq!(sta.status, Status::INVALID_OP); -} - -#[test] -fn is_negative() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_negative()); - let t = "-0x1p+0".parse::().unwrap(); - assert!(t.is_negative()); - - assert!(!Single::INFINITY.is_negative()); - assert!((-Single::INFINITY).is_negative()); - - assert!(!Single::ZERO.is_negative()); - assert!((-Single::ZERO).is_negative()); - - assert!(!Single::NAN.is_negative()); - assert!((-Single::NAN).is_negative()); - - assert!(!Single::snan(None).is_negative()); - assert!((-Single::snan(None)).is_negative()); -} - -#[test] -fn is_normal() { - let t = "0x1p+0".parse::().unwrap(); - assert!(t.is_normal()); - - assert!(!Single::INFINITY.is_normal()); - assert!(!Single::ZERO.is_normal()); - assert!(!Single::NAN.is_normal()); - assert!(!Single::snan(None).is_normal()); - assert!(!"0x1p-149".parse::().unwrap().is_normal()); -} - -#[test] -fn is_finite() { - let t = "0x1p+0".parse::().unwrap(); - assert!(t.is_finite()); - assert!(!Single::INFINITY.is_finite()); - assert!(Single::ZERO.is_finite()); - assert!(!Single::NAN.is_finite()); - assert!(!Single::snan(None).is_finite()); - assert!("0x1p-149".parse::().unwrap().is_finite()); -} - -#[test] -fn is_infinite() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_infinite()); - assert!(Single::INFINITY.is_infinite()); - assert!(!Single::ZERO.is_infinite()); - assert!(!Single::NAN.is_infinite()); - assert!(!Single::snan(None).is_infinite()); - assert!(!"0x1p-149".parse::().unwrap().is_infinite()); -} - -#[test] -fn is_nan() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_nan()); - assert!(!Single::INFINITY.is_nan()); - assert!(!Single::ZERO.is_nan()); - assert!(Single::NAN.is_nan()); - assert!(Single::snan(None).is_nan()); - assert!(!"0x1p-149".parse::().unwrap().is_nan()); -} - -#[test] -fn is_finite_non_zero() { - // Test positive/negative normal value. - assert!("0x1p+0".parse::().unwrap().is_finite_non_zero()); - assert!("-0x1p+0".parse::().unwrap().is_finite_non_zero()); - - // Test positive/negative denormal value. - assert!("0x1p-149".parse::().unwrap().is_finite_non_zero()); - assert!("-0x1p-149".parse::().unwrap().is_finite_non_zero()); - - // Test +/- Infinity. - assert!(!Single::INFINITY.is_finite_non_zero()); - assert!(!(-Single::INFINITY).is_finite_non_zero()); - - // Test +/- Zero. - assert!(!Single::ZERO.is_finite_non_zero()); - assert!(!(-Single::ZERO).is_finite_non_zero()); - - // Test +/- qNaN. +/- don't mean anything with qNaN but paranoia can't hurt in - // this instance. - assert!(!Single::NAN.is_finite_non_zero()); - assert!(!(-Single::NAN).is_finite_non_zero()); - - // Test +/- sNaN. +/- don't mean anything with sNaN but paranoia can't hurt in - // this instance. - assert!(!Single::snan(None).is_finite_non_zero()); - assert!(!(-Single::snan(None)).is_finite_non_zero()); -} - -#[test] -fn add() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "inf", Status::OK, Category::Infinity), - (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "inf", Status::OK, Category::Infinity), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "inf", Status::OK, Category::Infinity), - (p_zero, m_inf, "-inf", Status::OK, Category::Infinity), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_zero, p_inf, "inf", Status::OK, Category::Infinity), - (m_zero, m_inf, "-inf", Status::OK, Category::Infinity), - (m_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (m_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+1", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_normal_value, "-0x1p+1", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - p_largest_value, - p_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_largest_value, - m_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - m_largest_value, - p_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_largest_value, - m_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, p_smallest_value, "0x1p-148", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, p_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_smallest_value, "-0x1p-148", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - ( - p_smallest_normalized, - p_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_smallest_normalized, - m_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_normalized, p_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - ( - m_smallest_normalized, - p_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_smallest_normalized, - m_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_smallest_normalized, p_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x + y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn subtract() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_inf, "inf", Status::OK, Category::Infinity), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "inf", Status::OK, Category::Infinity), - (p_inf, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "-inf", Status::OK, Category::Infinity), - (p_zero, m_inf, "inf", Status::OK, Category::Infinity), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (p_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (m_zero, p_inf, "-inf", Status::OK, Category::Infinity), - (m_zero, m_inf, "inf", Status::OK, Category::Infinity), - (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_normal_value, "0x1p+1", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+1", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - p_largest_value, - p_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_largest_value, - m_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - m_largest_value, - p_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_largest_value, - m_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_smallest_value, "0x1p-148", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, p_smallest_value, "-0x1p-148", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, p_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - ( - p_smallest_normalized, - p_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_smallest_normalized, - m_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_normalized, p_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), - (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - ( - m_smallest_normalized, - p_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_smallest_normalized, - m_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_smallest_normalized, p_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x - y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn multiply() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - let underflow_status = Status::UNDERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "inf", Status::OK, Category::Infinity), - (p_inf, m_inf, "-inf", Status::OK, Category::Infinity), - (p_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, m_inf, "inf", Status::OK, Category::Infinity), - (m_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_normal_value, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_normal_value, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_normal_value, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_normal_value, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (p_largest_value, m_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (p_largest_value, p_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_largest_value, m_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (m_largest_value, m_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (m_largest_value, p_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_largest_value, m_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, p_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (p_smallest_value, m_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, p_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (m_smallest_value, m_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_normalized, m_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_smallest_normalized, m_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x * y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn divide() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - let underflow_status = Status::UNDERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "inf", Status::OK, Category::Infinity), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_normal_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_normal_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "0x1p-128", underflow_status, Category::Normal), - (p_normal_value, m_largest_value, "-0x1p-128", underflow_status, Category::Normal), - (p_normal_value, p_smallest_value, "inf", overflow_status, Category::Infinity), - (p_normal_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), - (p_normal_value, p_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), - (p_normal_value, m_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), - (m_normal_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_normal_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "-0x1p-128", underflow_status, Category::Normal), - (m_normal_value, m_largest_value, "0x1p-128", underflow_status, Category::Normal), - (m_normal_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), - (m_normal_value, m_smallest_value, "inf", overflow_status, Category::Infinity), - (m_normal_value, p_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), - (m_normal_value, m_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), - (p_largest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_largest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_largest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, p_largest_value, "0x1p+0", Status::OK, Category::Normal), - (p_largest_value, m_largest_value, "-0x1p+0", Status::OK, Category::Normal), - (p_largest_value, p_smallest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_normalized, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_smallest_normalized, "-inf", overflow_status, Category::Infinity), - (m_largest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_largest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, p_largest_value, "-0x1p+0", Status::OK, Category::Normal), - (m_largest_value, m_largest_value, "0x1p+0", Status::OK, Category::Normal), - (m_largest_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_smallest_value, "inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_normalized, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_smallest_normalized, "inf", overflow_status, Category::Infinity), - (p_smallest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, p_largest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, p_smallest_value, "0x1p+0", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_value, "-0x1p+0", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_largest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_smallest_value, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_value, "0x1p+0", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_normalized, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_largest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_smallest_value, "0x1p+23", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "-0x1p+23", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_normalized, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_normalized, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_largest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_smallest_value, "-0x1p+23", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "0x1p+23", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x / y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn operator_overloads() { - // This is mostly testing that these operator overloads compile. - let one = "0x1p+0".parse::().unwrap(); - let two = "0x2p+0".parse::().unwrap(); - assert!(two.bitwise_eq((one + one).value)); - assert!(one.bitwise_eq((two - one).value)); - assert!(two.bitwise_eq((one * two).value)); - assert!(one.bitwise_eq((two / two).value)); -} - -#[test] -fn abs() { - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let p_qnan = Single::NAN; - let m_qnan = -Single::NAN; - let p_snan = Single::snan(None); - let m_snan = -Single::snan(None); - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - assert!(p_inf.bitwise_eq(p_inf.abs())); - assert!(p_inf.bitwise_eq(m_inf.abs())); - assert!(p_zero.bitwise_eq(p_zero.abs())); - assert!(p_zero.bitwise_eq(m_zero.abs())); - assert!(p_qnan.bitwise_eq(p_qnan.abs())); - assert!(p_qnan.bitwise_eq(m_qnan.abs())); - assert!(p_snan.bitwise_eq(p_snan.abs())); - assert!(p_snan.bitwise_eq(m_snan.abs())); - assert!(p_normal_value.bitwise_eq(p_normal_value.abs())); - assert!(p_normal_value.bitwise_eq(m_normal_value.abs())); - assert!(p_largest_value.bitwise_eq(p_largest_value.abs())); - assert!(p_largest_value.bitwise_eq(m_largest_value.abs())); - assert!(p_smallest_value.bitwise_eq(p_smallest_value.abs())); - assert!(p_smallest_value.bitwise_eq(m_smallest_value.abs())); - assert!(p_smallest_normalized.bitwise_eq(p_smallest_normalized.abs(),)); - assert!(p_smallest_normalized.bitwise_eq(m_smallest_normalized.abs(),)); -} - -#[test] -fn neg() { - let one = "1.0".parse::().unwrap(); - let neg_one = "-1.0".parse::().unwrap(); - let zero = Single::ZERO; - let neg_zero = -Single::ZERO; - let inf = Single::INFINITY; - let neg_inf = -Single::INFINITY; - let qnan = Single::NAN; - let neg_qnan = -Single::NAN; - - assert!(neg_one.bitwise_eq(-one)); - assert!(one.bitwise_eq(-neg_one)); - assert!(neg_zero.bitwise_eq(-zero)); - assert!(zero.bitwise_eq(-neg_zero)); - assert!(neg_inf.bitwise_eq(-inf)); - assert!(inf.bitwise_eq(-neg_inf)); - assert!(neg_inf.bitwise_eq(-inf)); - assert!(inf.bitwise_eq(-neg_inf)); - assert!(neg_qnan.bitwise_eq(-qnan)); - assert!(qnan.bitwise_eq(-neg_qnan)); -} - -#[test] -fn ilogb() { - assert_eq!(-1074, Double::SMALLEST.ilogb()); - assert_eq!(-1074, (-Double::SMALLEST).ilogb()); - assert_eq!(-1023, "0x1.ffffffffffffep-1024".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.ffffffffffffep-1023".parse::().unwrap().ilogb()); - assert_eq!(-1023, "-0x1.ffffffffffffep-1023".parse::().unwrap().ilogb()); - assert_eq!(-51, "0x1p-51".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.c60f120d9f87cp-1023".parse::().unwrap().ilogb()); - assert_eq!(-2, "0x0.ffffp-1".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.fffep-1023".parse::().unwrap().ilogb()); - assert_eq!(1023, Double::largest().ilogb()); - assert_eq!(1023, (-Double::largest()).ilogb()); - - assert_eq!(0, "0x1p+0".parse::().unwrap().ilogb()); - assert_eq!(0, "-0x1p+0".parse::().unwrap().ilogb()); - assert_eq!(42, "0x1p+42".parse::().unwrap().ilogb()); - assert_eq!(-42, "0x1p-42".parse::().unwrap().ilogb()); - - assert_eq!(IEK_INF, Single::INFINITY.ilogb()); - assert_eq!(IEK_INF, (-Single::INFINITY).ilogb()); - assert_eq!(IEK_ZERO, Single::ZERO.ilogb()); - assert_eq!(IEK_ZERO, (-Single::ZERO).ilogb()); - assert_eq!(IEK_NAN, Single::NAN.ilogb()); - assert_eq!(IEK_NAN, Single::snan(None).ilogb()); - - assert_eq!(127, Single::largest().ilogb()); - assert_eq!(127, (-Single::largest()).ilogb()); - - assert_eq!(-149, Single::SMALLEST.ilogb()); - assert_eq!(-149, (-Single::SMALLEST).ilogb()); - assert_eq!(-126, Single::smallest_normalized().ilogb()); - assert_eq!(-126, (-Single::smallest_normalized()).ilogb()); -} - -#[test] -fn scalbn() { - assert!( - "0x1p+0" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(0),) - ); - assert!( - "0x1p+42" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(42),) - ); - assert!( - "0x1p-42" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(-42),) - ); - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let p_qnan = Single::NAN; - let m_qnan = -Single::NAN; - let snan = Single::snan(None); - - assert!(p_inf.bitwise_eq(p_inf.scalbn(0))); - assert!(m_inf.bitwise_eq(m_inf.scalbn(0))); - assert!(p_zero.bitwise_eq(p_zero.scalbn(0))); - assert!(m_zero.bitwise_eq(m_zero.scalbn(0))); - assert!(p_qnan.bitwise_eq(p_qnan.scalbn(0))); - assert!(m_qnan.bitwise_eq(m_qnan.scalbn(0))); - assert!(!snan.scalbn(0).is_signaling()); - - let scalbn_snan = snan.scalbn(1); - assert!(scalbn_snan.is_nan() && !scalbn_snan.is_signaling()); - - // Make sure highest bit of payload is preserved. - let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; - - let snan_with_payload = Double::snan(Some(payload)); - let quiet_payload = snan_with_payload.scalbn(1); - assert!(quiet_payload.is_nan() && !quiet_payload.is_signaling()); - assert_eq!(payload, quiet_payload.to_bits() & ((1 << 51) - 1)); - - assert!(p_inf.bitwise_eq("0x1p+0".parse::().unwrap().scalbn(128),)); - assert!(m_inf.bitwise_eq("-0x1p+0".parse::().unwrap().scalbn(128),)); - assert!(p_inf.bitwise_eq("0x1p+127".parse::().unwrap().scalbn(1),)); - assert!(p_zero.bitwise_eq("0x1p-127".parse::().unwrap().scalbn(-127),)); - assert!(m_zero.bitwise_eq("-0x1p-127".parse::().unwrap().scalbn(-127),)); - assert!( - "-0x1p-149" - .parse::() - .unwrap() - .bitwise_eq("-0x1p-127".parse::().unwrap().scalbn(-22),) - ); - assert!(p_zero.bitwise_eq("0x1p-126".parse::().unwrap().scalbn(-24),)); - - let smallest_f64 = Double::SMALLEST; - let neg_smallest_f64 = -Double::SMALLEST; - - let largest_f64 = Double::largest(); - let neg_largest_f64 = -Double::largest(); - - let largest_denormal_f64 = "0x1.ffffffffffffep-1023".parse::().unwrap(); - let neg_largest_denormal_f64 = "-0x1.ffffffffffffep-1023".parse::().unwrap(); - - assert!(smallest_f64.bitwise_eq("0x1p-1074".parse::().unwrap().scalbn(0),)); - assert!(neg_smallest_f64.bitwise_eq("-0x1p-1074".parse::().unwrap().scalbn(0),)); - - assert!("0x1p+1023".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),)); - - assert!(smallest_f64.scalbn(-2097).is_pos_zero()); - assert!(smallest_f64.scalbn(-2098).is_pos_zero()); - assert!(smallest_f64.scalbn(-2099).is_pos_zero()); - assert!("0x1p+1022".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2096,),)); - assert!("0x1p+1023".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),)); - assert!(smallest_f64.scalbn(2098).is_infinite()); - assert!(smallest_f64.scalbn(2099).is_infinite()); - - // Test for integer overflows when adding to exponent. - assert!(smallest_f64.scalbn(-ExpInt::MAX).is_pos_zero()); - assert!(largest_f64.scalbn(ExpInt::MAX).is_infinite()); - - assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),)); - assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),)); - - assert!( - "0x1.ffffffffffffep-1022" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1)) - ); - assert!( - "-0x1.ffffffffffffep-1021" - .parse::() - .unwrap() - .bitwise_eq(neg_largest_denormal_f64.scalbn(2)) - ); - - assert!( - "0x1.ffffffffffffep+1" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1024)) - ); - assert!(largest_denormal_f64.scalbn(-1023).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(-1024).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(-2048).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(2047).is_infinite()); - assert!(largest_denormal_f64.scalbn(2098).is_infinite()); - assert!(largest_denormal_f64.scalbn(2099).is_infinite()); - - assert!( - "0x1.ffffffffffffep-2" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1021)) - ); - assert!( - "0x1.ffffffffffffep-1" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1022)) - ); - assert!( - "0x1.ffffffffffffep+0" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1023)) - ); - assert!( - "0x1.ffffffffffffep+1023" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(2046)) - ); - assert!("0x1p+974".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2048,),)); - - let random_denormal_f64 = "0x1.c60f120d9f87cp+51".parse::().unwrap(); - assert!( - "0x1.c60f120d9f87cp-972" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-1023)) - ); - assert!( - "0x1.c60f120d9f87cp-1" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-52)) - ); - assert!( - "0x1.c60f120d9f87cp-2" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-53)) - ); - assert!( - "0x1.c60f120d9f87cp+0" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-51)) - ); - - assert!(random_denormal_f64.scalbn(-2097).is_pos_zero()); - assert!(random_denormal_f64.scalbn(-2090).is_pos_zero()); - - assert!("-0x1p-1073".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2097),)); - - assert!("-0x1p-1024".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2048),)); - - assert!("0x1p-1073".parse::().unwrap().bitwise_eq(largest_f64.scalbn(-2097,),)); - - assert!("0x1p-1074".parse::().unwrap().bitwise_eq(largest_f64.scalbn(-2098,),)); - assert!("-0x1p-1074".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2098),)); - assert!(neg_largest_f64.scalbn(-2099).is_neg_zero()); - assert!(largest_f64.scalbn(1).is_infinite()); - - assert!( - "0x1p+0" - .parse::() - .unwrap() - .bitwise_eq("0x1p+52".parse::().unwrap().scalbn(-52),) - ); - - assert!( - "0x1p-103" - .parse::() - .unwrap() - .bitwise_eq("0x1p-51".parse::().unwrap().scalbn(-52),) - ); -} - -#[test] -fn frexp() { - let p_zero = Double::ZERO; - let m_zero = -Double::ZERO; - let one = Double::from_f64(1.0); - let m_one = Double::from_f64(-1.0); - - let largest_denormal = "0x1.ffffffffffffep-1023".parse::().unwrap(); - let neg_largest_denormal = "-0x1.ffffffffffffep-1023".parse::().unwrap(); - - let smallest = Double::SMALLEST; - let neg_smallest = -Double::SMALLEST; - - let largest = Double::largest(); - let neg_largest = -Double::largest(); - - let p_inf = Double::INFINITY; - let m_inf = -Double::INFINITY; - - let p_qnan = Double::NAN; - let m_qnan = -Double::NAN; - let snan = Double::snan(None); - - // Make sure highest bit of payload is preserved. - let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; - - let snan_with_payload = Double::snan(Some(payload)); - - let mut exp = 0; - - let frac = p_zero.frexp(&mut exp); - assert_eq!(0, exp); - assert!(frac.is_pos_zero()); - - let frac = m_zero.frexp(&mut exp); - assert_eq!(0, exp); - assert!(frac.is_neg_zero()); - - let frac = one.frexp(&mut exp); - assert_eq!(1, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = m_one.frexp(&mut exp); - assert_eq!(1, exp); - assert!("-0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("0x1.ffffffffffffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("-0x1.ffffffffffffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("-0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("0x1.fffffffffffffp-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("-0x1.fffffffffffffp-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = p_inf.frexp(&mut exp); - assert_eq!(IEK_INF, exp); - assert!(frac.is_infinite() && !frac.is_negative()); - - let frac = m_inf.frexp(&mut exp); - assert_eq!(IEK_INF, exp); - assert!(frac.is_infinite() && frac.is_negative()); - - let frac = p_qnan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan()); - - let frac = m_qnan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan()); - - let frac = snan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan() && !frac.is_signaling()); - - let frac = snan_with_payload.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan() && !frac.is_signaling()); - assert_eq!(payload, frac.to_bits() & ((1 << 51) - 1)); - - let frac = "0x0.ffffp-1".parse::().unwrap().frexp(&mut exp); - assert_eq!(-1, exp); - assert!("0x1.fffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = "0x1p-51".parse::().unwrap().frexp(&mut exp); - assert_eq!(-50, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = "0x1.c60f120d9f87cp+51".parse::().unwrap().frexp(&mut exp); - assert_eq!(52, exp); - assert!("0x1.c60f120d9f87cp-1".parse::().unwrap().bitwise_eq(frac)); -} - -#[test] -fn modulo() { - let mut status; - { - let f1 = "1.5".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.5".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.5".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.5".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1.3333333333333p-2".parse::().unwrap(); // 0.3 - let f2 = "0x1.47ae147ae147bp-7".parse::().unwrap(); // 0.01 - // 0.009999999999999983 - let expected = "0x1.47ae147ae1471p-7".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p64".parse::().unwrap(); // 1.8446744073709552e19 - let f2 = "1.5".parse::().unwrap(); - let expected = "1.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p1000".parse::().unwrap(); - let f2 = "0x1p-1000".parse::().unwrap(); - let expected = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.0".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "1.0".parse::().unwrap(); - let f2 = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = "0.0".parse::().unwrap(); - let f2 = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = Double::INFINITY; - let f2 = "1.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } -} diff --git a/compiler/rustc_apfloat/tests/ppc.rs b/compiler/rustc_apfloat/tests/ppc.rs deleted file mode 100644 index c769d265437..00000000000 --- a/compiler/rustc_apfloat/tests/ppc.rs +++ /dev/null @@ -1,530 +0,0 @@ -use rustc_apfloat::ppc::DoubleDouble; -use rustc_apfloat::{Category, Float, Round}; - -use std::cmp::Ordering; - -#[test] -fn ppc_double_double() { - let test = DoubleDouble::ZERO; - let expected = "0x0p+0".parse::().unwrap(); - assert!(test.is_zero()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(0, test.to_bits()); - - let test = -DoubleDouble::ZERO; - let expected = "-0x0p+0".parse::().unwrap(); - assert!(test.is_zero()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(0x8000000000000000, test.to_bits()); - - let test = "1.0".parse::().unwrap(); - assert_eq!(0x3ff0000000000000, test.to_bits()); - - // LDBL_MAX - let test = "1.79769313486231580793728971405301e+308".parse::().unwrap(); - assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits()); - - // LDBL_MIN - let test = "2.00416836000897277799610805135016e-292".parse::().unwrap(); - assert_eq!(0x0000000000000000_0360000000000000, test.to_bits()); -} - -#[test] -fn ppc_double_double_add_special() { - let data = [ - // (1 + 0) + (-1 + 0) = Category::Zero - (0x3ff0000000000000, 0xbff0000000000000, Category::Zero, Round::NearestTiesToEven), - // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x7948000000000000, - Category::Infinity, - Round::NearestTiesToEven, - ), - // FIXME: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when - // DoubleDouble's fallback is gone. - // LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 - - // 160))) = Category::Normal - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x75effffffffffffe_7947ffffffffffff, - Category::Normal, - Round::NearestTiesToEven, - ), - // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x7c8ffffffffffffe_7fefffffffffffff, - Category::Infinity, - Round::NearestTiesToEven, - ), - // NaN + (1 + 0) = Category::NaN - (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.add_r(a2, round).value; - - assert_eq!(expected, a1.category(), "{:#x} + {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.add_r(a1, round).value; - - assert_eq!(expected, a2.category(), "{:#x} + {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_add() { - let data = [ - // (1 + 0) + (1e-105 + 0) = (1 + 1e-105) - ( - 0x3ff0000000000000, - 0x3960000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) + (1e-106 + 0) = (1 + 1e-106) - ( - 0x3ff0000000000000, - 0x3950000000000000, - 0x3950000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105) - ( - 0x3950000000000000_3ff0000000000000, - 0x3950000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) + (epsilon + 0) = (1 + epsilon) - ( - 0x3ff0000000000000, - 0x0000000000000001, - 0x0000000000000001_3ff0000000000000, - Round::NearestTiesToEven, - ), - // FIXME: change 0xf950000000000000 to 0xf940000000000000, when - // DoubleDouble's fallback is gone. - // (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX + - // 1.11111... << (1023 - 52) - ( - 0xf950000000000000_7fefffffffffffff, - 0x7c90000000000000, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - // FIXME: change 0xf950000000000000 to 0xf940000000000000, when - // DoubleDouble's fallback is gone. - // (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX + - // 1.11111... << (1023 - 52) - ( - 0x7c90000000000000, - 0xf950000000000000_7fefffffffffffff, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.add_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} + {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.add_r(a1, round).value; - - assert_eq!(expected, a2.to_bits(), "{:#x} + {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_subtract() { - let data = [ - // (1 + 0) - (-1e-105 + 0) = (1 + 1e-105) - ( - 0x3ff0000000000000, - 0xb960000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) - (-1e-106 + 0) = (1 + 1e-106) - ( - 0x3ff0000000000000, - 0xb950000000000000, - 0x3950000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.sub_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} - {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_multiply_special() { - let data = [ - // Category::NaN * Category::NaN = Category::NaN - (0x7ff8000000000000, 0x7ff8000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Zero = Category::NaN - (0x7ff8000000000000, 0, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Infinity = Category::NaN - (0x7ff8000000000000, 0x7ff0000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Normal = Category::NaN - (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::Infinity * Category::Infinity = Category::Infinity - (0x7ff0000000000000, 0x7ff0000000000000, Category::Infinity, Round::NearestTiesToEven), - // Category::Infinity * Category::Zero = Category::NaN - (0x7ff0000000000000, 0, Category::NaN, Round::NearestTiesToEven), - // Category::Infinity * Category::Normal = Category::Infinity - (0x7ff0000000000000, 0x3ff0000000000000, Category::Infinity, Round::NearestTiesToEven), - // Category::Zero * Category::Zero = Category::Zero - (0, 0, Category::Zero, Round::NearestTiesToEven), - // Category::Zero * Category::Normal = Category::Zero - (0, 0x3ff0000000000000, Category::Zero, Round::NearestTiesToEven), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.mul_r(a2, round).value; - - assert_eq!(expected, a1.category(), "{:#x} * {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.mul_r(a1, round).value; - - assert_eq!(expected, a2.category(), "{:#x} * {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_multiply() { - let data = [ - // 1/3 * 3 = 1.0 - ( - 0x3c75555555555556_3fd5555555555555, - 0x4008000000000000, - 0x3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + epsilon) * (1 + 0) = Category::Zero - ( - 0x0000000000000001_3ff0000000000000, - 0x3ff0000000000000, - 0x0000000000000001_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon - ( - 0x0000000000000001_3ff0000000000000, - 0x0000000000000001_3ff0000000000000, - 0x0000000000000002_3ff0000000000000, - Round::NearestTiesToEven, - ), - // -(1 + epsilon) * (1 + epsilon) = -1 - ( - 0x0000000000000001_bff0000000000000, - 0x0000000000000001_3ff0000000000000, - 0xbff0000000000000, - Round::NearestTiesToEven, - ), - // (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon - ( - 0x3fe0000000000000, - 0x0000000000000002_3ff0000000000000, - 0x0000000000000001_3fe0000000000000, - Round::NearestTiesToEven, - ), - // (0.5 + 0) * (1 + epsilon) = 0.5 - ( - 0x3fe0000000000000, - 0x0000000000000001_3ff0000000000000, - 0x3fe0000000000000, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 106) = inf - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3950000000000000_3ff0000000000000, - 0x7ff0000000000000, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=||| - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3940000000000000_3ff0000000000000, - 0x7c8fffffffffffff_7fefffffffffffff, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__ - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3930000000000000_3ff0000000000000, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.mul_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} * {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.mul_r(a1, round).value; - - assert_eq!(expected, a2.to_bits(), "{:#x} * {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_divide() { - // FIXME: Only a sanity check for now. Add more edge cases when the - // double-double algorithm is implemented. - let data = [ - // 1 / 3 = 1/3 - ( - 0x3ff0000000000000, - 0x4008000000000000, - 0x3c75555555555556_3fd5555555555555, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.div_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} / {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_remainder() { - let data = [ - // ieee_rem(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3ca4000000000000_3ff4000000000000, - 0x3c90000000000000_3fe0000000000000, - ), - // ieee_rem(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3cac000000000000_3ffc000000000000, - 0xbc90000000000000_bfe0000000000000, - ), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - let result = a1.ieee_rem(a2).value; - - assert_eq!(expected, result.to_bits(), "ieee_rem({:#x}, {:#x})", op1, op2); - } -} - -#[test] -fn ppc_double_double_mod() { - let data = [ - // mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3ca4000000000000_3ff4000000000000, - 0x3c90000000000000_3fe0000000000000, - ), - // mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53) - // 0xbc98000000000000 doesn't seem right, but it's what we currently have. - // FIXME: investigate - ( - 0x3cb8000000000000_4008000000000000, - 0x3cac000000000000_3ffc000000000000, - 0xbc98000000000000_3ff4000000000001, - ), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - let r = (a1 % a2).value; - - assert_eq!(expected, r.to_bits(), "fmod({:#x}, {:#x})", op1, op2); - } -} - -#[test] -fn ppc_double_double_fma() { - // Sanity check for now. - let mut a = "2".parse::().unwrap(); - a = a.mul_add("3".parse::().unwrap(), "4".parse::().unwrap()).value; - assert_eq!(Some(Ordering::Equal), "10".parse::().unwrap().partial_cmp(&a)); -} - -#[test] -fn ppc_double_double_round_to_integral() { - { - let a = "1.5".parse::().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::().unwrap().partial_cmp(&a)); - } - { - let a = "2.5".parse::().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::().unwrap().partial_cmp(&a)); - } -} - -#[test] -fn ppc_double_double_compare() { - let data = [ - // (1 + 0) = (1 + 0) - (0x3ff0000000000000, 0x3ff0000000000000, Some(Ordering::Equal)), - // (1 + 0) < (1.00...1 + 0) - (0x3ff0000000000000, 0x3ff0000000000001, Some(Ordering::Less)), - // (1.00...1 + 0) > (1 + 0) - (0x3ff0000000000001, 0x3ff0000000000000, Some(Ordering::Greater)), - // (1 + 0) < (1 + epsilon) - (0x3ff0000000000000, 0x0000000000000001_3ff0000000000001, Some(Ordering::Less)), - // NaN != NaN - (0x7ff8000000000000, 0x7ff8000000000000, None), - // (1 + 0) != NaN - (0x3ff0000000000000, 0x7ff8000000000000, None), - // Inf = Inf - (0x7ff0000000000000, 0x7ff0000000000000, Some(Ordering::Equal)), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - assert_eq!(expected, a1.partial_cmp(&a2), "compare({:#x}, {:#x})", op1, op2,); - } -} - -#[test] -fn ppc_double_double_bitwise_eq() { - let data = [ - // (1 + 0) = (1 + 0) - (0x3ff0000000000000, 0x3ff0000000000000, true), - // (1 + 0) != (1.00...1 + 0) - (0x3ff0000000000000, 0x3ff0000000000001, false), - // NaN = NaN - (0x7ff8000000000000, 0x7ff8000000000000, true), - // NaN != NaN with a different bit pattern - (0x7ff8000000000000, 0x3ff0000000000000_7ff8000000000000, false), - // Inf = Inf - (0x7ff0000000000000, 0x7ff0000000000000, true), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - assert_eq!(expected, a1.bitwise_eq(a2), "{:#x} = {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_change_sign() { - let float = DoubleDouble::from_bits(0xbcb0000000000000_400f000000000000); - { - let actual = float.copy_sign("1".parse::().unwrap()); - assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits()); - } - { - let actual = float.copy_sign("-1".parse::().unwrap()); - assert_eq!(0x3cb0000000000000_c00f000000000000, actual.to_bits()); - } -} - -#[test] -fn ppc_double_double_factories() { - assert_eq!(0, DoubleDouble::ZERO.to_bits()); - assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, DoubleDouble::largest().to_bits()); - assert_eq!(0x0000000000000001, DoubleDouble::SMALLEST.to_bits()); - assert_eq!(0x0360000000000000, DoubleDouble::smallest_normalized().to_bits()); - assert_eq!(0x0000000000000000_8000000000000000, (-DoubleDouble::ZERO).to_bits()); - assert_eq!(0xfc8ffffffffffffe_ffefffffffffffff, (-DoubleDouble::largest()).to_bits()); - assert_eq!(0x0000000000000000_8000000000000001, (-DoubleDouble::SMALLEST).to_bits()); - assert_eq!( - 0x0000000000000000_8360000000000000, - (-DoubleDouble::smallest_normalized()).to_bits() - ); - assert!(DoubleDouble::SMALLEST.is_smallest()); - assert!(DoubleDouble::largest().is_largest()); -} - -#[test] -fn ppc_double_double_is_denormal() { - assert!(DoubleDouble::SMALLEST.is_denormal()); - assert!(!DoubleDouble::largest().is_denormal()); - assert!(!DoubleDouble::smallest_normalized().is_denormal()); - { - // (4 + 3) is not normalized - let data = 0x4008000000000000_4010000000000000; - assert!(DoubleDouble::from_bits(data).is_denormal()); - } -} - -#[test] -fn ppc_double_double_exact_inverse() { - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); -} - -#[test] -fn ppc_double_double_scalbn() { - // 3.0 + 3.0 << 53 - let input = 0x3cb8000000000000_4008000000000000; - let result = DoubleDouble::from_bits(input).scalbn(1); - // 6.0 + 6.0 << 53 - assert_eq!(0x3cc8000000000000_4018000000000000, result.to_bits()); -} - -#[test] -fn ppc_double_double_frexp() { - // 3.0 + 3.0 << 53 - let input = 0x3cb8000000000000_4008000000000000; - let mut exp = 0; - // 0.75 + 0.75 << 53 - let result = DoubleDouble::from_bits(input).frexp(&mut exp); - assert_eq!(2, exp); - assert_eq!(0x3c98000000000000_3fe8000000000000, result.to_bits()); -} diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 74030a43c50..4e47fed8640 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tracing = "0.1" either = "1" -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 4c238308fe8..bb8e774cea3 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -13,7 +13,7 @@ gsgdt = "0.1.2" field-offset = "0.3.5" measureme = "10.0.0" polonius-engine = "0.13.0" -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 58449ee9eb4..c7e2c625ce5 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -10,7 +10,7 @@ rustc_arena = { path = "../rustc_arena" } tracing = "0.1" either = "1" rustc_middle = { path = "../rustc_middle" } -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_errors = { path = "../rustc_errors" } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index bfb967213e1..57cbfe68be4 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -46,6 +46,7 @@ const EXCEPTIONS: &[(&str, &str)] = &[ ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot ("mdbook", "MPL-2.0"), // mdbook ("openssl", "Apache-2.0"), // opt-dist + ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses) ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde) ("self_cell", "Apache-2.0"), // rustc (fluent translations) ("snap", "BSD-3-Clause"), // rustc @@ -224,6 +225,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustc-hash", "rustc-rayon", "rustc-rayon-core", + "rustc_apfloat", "rustc_version", "rustix", "ruzstd", // via object in thorin-dwp diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index d0257d71697..11480e2be60 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -302,10 +302,6 @@ pub fn check(path: &Path, bad: &mut bool) { return; } } - // apfloat shouldn't be changed because of license problems - if is_in(file, "compiler", "rustc_apfloat") { - return; - } let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); let mut skip_undocumented_unsafe = contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); diff --git a/triagebot.toml b/triagebot.toml index cc26c3b3e7b..c947c3f1930 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -318,14 +318,6 @@ changelog-branch = "master" [shortcut] - -[mentions."compiler/rustc_apfloat"] -message = """ -Changes rustc_apfloat. rustc_apfloat is currently in limbo and you almost \ -certainly don't want to change it (see #55993). -""" -cc = ["@eddyb"] - [mentions."compiler/rustc_codegen_cranelift"] cc = ["@bjorn3"] @@ -609,7 +601,6 @@ style-team = [ "/Cargo.lock" = ["@Mark-Simulacrum"] "/Cargo.toml" = ["@Mark-Simulacrum"] "/compiler" = ["compiler"] -"/compiler/rustc_apfloat" = ["@eddyb"] "/compiler/rustc_ast" = ["compiler", "parser"] "/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"] "/compiler/rustc_hir_analysis" = ["compiler", "types"]