From f450734a0bd2549065b35dcc89be1a0c262b3879 Mon Sep 17 00:00:00 2001 From: Jos Date: Wed, 17 Aug 2022 16:30:03 +0200 Subject: [PATCH] Fix off-by-one error in X87DoubleExtended::from_bits The standard floating point formats use an implicit bit in the significand. For example, for a 64-bit floating point value (a 'double'), the significand might be a 53-bit value, stored as 52 bits. The most significant bit is implicitly assumed to be 1. The X87 80-bit floating point format does not use an implicit bit. It stores a significand of 64 bits as 64 bits. The Semantics::PRECISION constant defines the size of the significand including the implicit bit. So for a 64-bit floating point value, Semantics::PRECISION would be 53 even though only 52 bits are used to store the significand. The code for the standard floating point formats has to work around this, by subtracting 1 from PRECISION to compute the correct number of bits. The code in X87DoubleExtended::from_bits incorrectly also subtracted 1 from PRECISION, even though no implicit bit is used in this format. Thus computing a size that is off-by-one from the actual size. --- compiler/rustc_apfloat/src/ieee.rs | 2 +- compiler/rustc_apfloat/tests/ieee.rs | 44 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs index 71bcb8f090d07..6f891abff5e2b 100644 --- a/compiler/rustc_apfloat/src/ieee.rs +++ b/compiler/rustc_apfloat/src/ieee.rs @@ -192,7 +192,7 @@ impl Semantics for X87DoubleExtendedS { let sign = bits & (1 << (Self::BITS - 1)); let exponent = (bits & !sign) >> Self::PRECISION; let mut r = IeeeFloat { - sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)], + sig: [bits & ((1 << Self::PRECISION) - 1)], // Convert the exponent from its bias representation to a signed integer. exp: (exponent as ExpInt) - Self::MAX_EXP, category: Category::Zero, diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs index 63d925cce9ad7..75c7d3899afe5 100644 --- a/compiler/rustc_apfloat/tests/ieee.rs +++ b/compiler/rustc_apfloat/tests/ieee.rs @@ -3299,3 +3299,47 @@ fn modulo() { assert_eq!(status, Status::INVALID_OP); } } + +#[test] +fn roundtrip() { + let f1 = Half::from_str_r("3.14159265358979323", Round::TowardZero).unwrap().value; + let bits1 = Half::to_bits(f1); + let f2 = Half::from_bits(bits1); + let bits2 = Half::to_bits(f2); + assert_eq!(bits1, bits2); + assert_eq!(f1, f2); + + let f1 = Single::from_str_r("3.14159265358979323", Round::TowardZero).unwrap().value; + let bits1 = Single::to_bits(f1); + let f2 = Single::from_bits(bits1); + let bits2 = Single::to_bits(f2); + assert_eq!(bits1, bits2); + assert_eq!(f1, f2); + + let f1 = Double::from_str_r("3.14159265358979323", Round::TowardZero).unwrap().value; + let bits1 = Double::to_bits(f1); + let f2 = Double::from_bits(bits1); + let bits2 = Double::to_bits(f2); + assert_eq!(bits1, bits2); + assert_eq!(f1, f2); + + let f1 = Quad::from_str_r("3.14159265358979323", Round::TowardZero).unwrap().value; + let bits1 = Quad::to_bits(f1); + let f2 = Quad::from_bits(bits1); + let bits2 = Quad::to_bits(f2); + assert_eq!(bits1, bits2); + assert_eq!(f1, f2); + + let f1 = X87DoubleExtended::from_str_r("3.14159265358979323", Round::TowardZero).unwrap().value; + let bits1 = X87DoubleExtended::to_bits(f1); + let f2 = X87DoubleExtended::from_bits(bits1); + let bits2 = X87DoubleExtended::to_bits(f2); + assert_eq!(bits1, bits2); + assert_eq!(f1, f2); +} + +#[test] +fn from_bits() { + let f1 = X87DoubleExtended::from_bits(0x4000C90FDAA22168C235); + assert_eq!(&f1.to_string(), "3.14159265358979323851"); +}