From 15e9f560880a9873c399194003335be328f4d705 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 18 Jul 2023 15:22:56 -0400 Subject: [PATCH 1/5] 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 9a59f455fe9af..5546a7cf39112 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 72f0236fd08a1..cc5c163b8bea0 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 98305201bc943..0000000000000 --- 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 2286712f02565..0000000000000 --- 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 dde368e7b924f..0000000000000 --- 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 65a0f66645bef..0000000000000 --- 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 f8fac0c2358c9..0000000000000 --- 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 c769d26543786..0000000000000 --- 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 74030a43c5053..4e47fed864045 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 4c238308fe876..bb8e774cea3d1 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 58449ee9eb48d..c7e2c625ce57a 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 bfb967213e139..57cbfe68be46c 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 d0257d716973e..11480e2be603c 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 cc26c3b3e7b94..c947c3f19301e 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"] From 6f5d9603895bfb62eb335b8ba10a1f11a584bf25 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 18 Jul 2023 16:16:01 -0400 Subject: [PATCH 2/5] Fix miri --- src/tools/miri/src/shims/foreign_items.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index c753eadbbad35..763fa9235d04d 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -914,16 +914,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let x = this.read_scalar(x)?.to_f64()?; let exp = this.read_scalar(exp)?.to_i32()?; - // Saturating cast to i16. Even those are outside the valid exponent range so - // `scalbn` below will do its over/underflow handling. - let exp = if exp > i32::from(i16::MAX) { - i16::MAX - } else if exp < i32::from(i16::MIN) { - i16::MIN - } else { - exp.try_into().unwrap() - }; - let res = x.scalbn(exp); this.write_scalar(Scalar::from_f64(res), dest)?; } From 47a4484a78471e87862bdb340ada960b4d78fd30 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 18 Jul 2023 16:37:16 -0400 Subject: [PATCH 3/5] Remove unused NCSA license --- LICENSES/NCSA.txt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 LICENSES/NCSA.txt diff --git a/LICENSES/NCSA.txt b/LICENSES/NCSA.txt deleted file mode 100644 index cf5413effa25a..0000000000000 --- a/LICENSES/NCSA.txt +++ /dev/null @@ -1,15 +0,0 @@ -University of Illinois/NCSA Open Source License - -Copyright (c) . All rights reserved. - -Developed by: - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. - - * Neither the names of , nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. From 98dd81e4e306c85fc6e76893779e147ede0f3ddf Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 25 Mar 2023 00:59:57 +0800 Subject: [PATCH 4/5] Add test case for #109567 --- .../ui/numbers-arithmetic/apfloat-modulo-wrong.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs diff --git a/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs b/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs new file mode 100644 index 0000000000000..64ff1f8b1d2df --- /dev/null +++ b/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs @@ -0,0 +1,15 @@ +// run-pass +// check-run-results +// regression test for issue #109567 + +fn f() -> f64 { + std::hint::black_box(-1.0) % std::hint::black_box(-1.0) +} + +const G: f64 = -1.0 % -1.0; + +pub fn main() { + assert_eq!(-1, G.signum() as i32); + assert_eq!((-0.0_f64).to_bits(), G.to_bits()); + assert_eq!(f().signum(), G.signum()); +} From 9d3e35ceaaa6d38b0f5e2eb1b815fa0c2cf71262 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 7 Jul 2023 12:50:07 +0200 Subject: [PATCH 5/5] Add tests for #102403 and #113407 --- tests/ui/const_prop/apfloat-f64-roundtrip.rs | 9 +++++++++ .../ui/const_prop/apfloat-remainder-regression.rs | 15 +++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/ui/const_prop/apfloat-f64-roundtrip.rs create mode 100644 tests/ui/const_prop/apfloat-remainder-regression.rs diff --git a/tests/ui/const_prop/apfloat-f64-roundtrip.rs b/tests/ui/const_prop/apfloat-f64-roundtrip.rs new file mode 100644 index 0000000000000..9fb2ac96bebdb --- /dev/null +++ b/tests/ui/const_prop/apfloat-f64-roundtrip.rs @@ -0,0 +1,9 @@ +// run-pass +// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes +// min-llvm-version: 16.0 (requires APFloat fixes in LLVM) + +// Regression test for a broken MIR optimization (issue #113407). +pub fn main() { + let f = f64::from_bits(0x19873cc2) as f32; + assert_eq!(f.to_bits(), 0); +} diff --git a/tests/ui/const_prop/apfloat-remainder-regression.rs b/tests/ui/const_prop/apfloat-remainder-regression.rs new file mode 100644 index 0000000000000..08932c333fd85 --- /dev/null +++ b/tests/ui/const_prop/apfloat-remainder-regression.rs @@ -0,0 +1,15 @@ +// run-pass +// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes + +// Regression test for a broken MIR optimization (issue #102403). +pub fn f() -> f64 { + std::hint::black_box(-1.0) % std::hint::black_box(-1.0) +} + +pub fn g() -> f64 { + -1.0 % -1.0 +} + +pub fn main() { + assert_eq!(f().signum(), g().signum()); +}