Skip to content

Commit

Permalink
Advance the port to llvm/llvm-project@69f6098
Browse files Browse the repository at this point in the history
(last APFloat-related LLVM commit from 2018).

This commit is also notable as the last APFloat-related commit before the
LLVM relicensing in early 2019 (llvm/llvm-project@2946cd7).
  • Loading branch information
eddyb committed Jul 18, 2023
1 parent a7e2deb commit 3015f8f
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = ["fuzz"]

[workspace.package]
version = "0.0.1+llvm-768d6dd08783"
version = "0.0.2+llvm-69f6098e8931"
edition = "2021"
license = "Apache-2.0 WITH LLVM-exception"

Expand Down
10 changes: 7 additions & 3 deletions src/ieee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ impl Semantics for X87DoubleExtendedS {
/// 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.
/// exponent = 0, integer bit 1 ("pseudodenormal")
/// At the moment, the first three are treated as NaNs, the last one as Normal.
fn from_bits(bits: u128) -> IeeeFloat<Self> {
let sign = bits & (1 << (Self::BITS - 1));
let exponent = (bits & !sign) >> Self::PRECISION;
Expand All @@ -214,13 +214,17 @@ impl Semantics for X87DoubleExtendedS {
marker: PhantomData,
};

let integer_bit = r.sig[0] >> (Self::PRECISION - 1);

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)] {
} else if r.exp == Self::MAX_EXP + 1 && r.sig != [1 << (Self::PRECISION - 1)]
|| r.exp != Self::MAX_EXP + 1 && r.exp != Self::MIN_EXP - 1 && integer_bit == 0
{
// Sign, exponent, significand meaningless.
r.category = Category::NaN;
} else {
Expand Down
42 changes: 41 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Port of LLVM's APFloat software floating-point implementation from the
//! following C++ sources (please update commit hash when backporting):
//! https://github.com/llvm/llvm-project/commit/768d6dd08783440606da83dac490889329619898
//! https://github.com/llvm/llvm-project/commit/69f6098e89312b934ed87e4cd3603401a9b436b4
//! * `llvm/include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
//! * `llvm/lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
//! * `llvm/unittests/ADT/APFloatTest.cpp` -> `tests` directory
Expand Down Expand Up @@ -456,6 +456,46 @@ pub trait Float:
}
}

/// Implements IEEE 754-2018 minimum semantics. Returns the smaller of 2
/// arguments, propagating NaNs and treating -0 as less than +0.
fn minimum(self, other: Self) -> Self {
if self.is_nan() {
self
} else if other.is_nan() {
other
} else if self.is_zero() && other.is_zero() && self.is_negative() != other.is_negative() {
if self.is_negative() {
self
} else {
other
}
} else if other.partial_cmp(&self) == Some(Ordering::Less) {
other
} else {
self
}
}

/// Implements IEEE 754-2018 maximum semantics. Returns the larger of 2
/// arguments, propagating NaNs and treating -0 as less than +0.
fn maximum(self, other: Self) -> Self {
if self.is_nan() {
self
} else if other.is_nan() {
other
} else if self.is_zero() && other.is_zero() && self.is_negative() != other.is_negative() {
if self.is_negative() {
other
} else {
self
}
} else if self.partial_cmp(&other) == Some(Ordering::Less) {
other
} else {
self
}
}

/// IEEE-754R isSignMinus: Returns true if and only if the current value is
/// negative.
///
Expand Down
5 changes: 1 addition & 4 deletions tests/downstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,7 @@ fn fuzz_fma_with_expected_outputs() {
// found many examples in all ops, as the root issue was the handling of the
// bit-level encoding itself, but negation was the easiest op to test here).
pub const FUZZ_X87_F80_NEG_CASES_WITH_EXPECTED_OUTPUTS: &[(u128, u128)] = &[
(
0x01010101010100000000, /* 3.05337213397376214408E-4857 */
0x81010101010100000000, /* -3.05337213397376214408E-4857 */
),
(0x01010101010100000000 /* NaN */, 0xffff0101010100000000 /* NaN */),
(
0x0000ff7f2300ff000000, /* 6.71098449692300485303E-4932 */
0x8001ff7f2300ff000000, /* -6.71098449692300485303E-4932 */
Expand Down
88 changes: 68 additions & 20 deletions tests/ieee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,38 @@ fn max_num() {
assert_eq!(1.0, nan.max(f1).to_f64());
}

#[test]
fn minimum() {
let f1 = Double::from_f64(1.0);
let f2 = Double::from_f64(2.0);
let zp = Double::from_f64(0.0);
let zn = Double::from_f64(-0.0);
let nan = Double::NAN;

assert_eq!(1.0, f1.minimum(f2).to_f64());
assert_eq!(1.0, f2.minimum(f1).to_f64());
assert_eq!(-0.0, zp.minimum(zn).to_f64());
assert_eq!(-0.0, zn.minimum(zp).to_f64());
assert!(f1.minimum(nan).to_f64().is_nan());
assert!(nan.minimum(f1).to_f64().is_nan());
}

#[test]
fn maximum() {
let f1 = Double::from_f64(1.0);
let f2 = Double::from_f64(2.0);
let zp = Double::from_f64(0.0);
let zn = Double::from_f64(-0.0);
let nan = Double::NAN;

assert_eq!(2.0, f1.maximum(f2).to_f64());
assert_eq!(2.0, f2.maximum(f1).to_f64());
assert_eq!(0.0, zp.maximum(zn).to_f64());
assert_eq!(0.0, zn.maximum(zp).to_f64());
assert!(f1.maximum(nan).to_f64().is_nan());
assert!(nan.maximum(f1).to_f64().is_nan());
}

#[test]
fn denormal() {
// Test single precision
Expand Down Expand Up @@ -1003,6 +1035,7 @@ fn to_string() {
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));
assert_eq!("NaN", X87DoubleExtended::from_bits(1 << 64).to_string());
}

#[test]
Expand Down Expand Up @@ -1072,11 +1105,11 @@ fn to_integer() {

#[test]
fn nan() {
fn nanbits<T: Float>(signaling: bool, negative: bool, fill: u128) -> u128 {
fn nanbits_from_u128<T: Float>(signaling: bool, negative: bool, payload: u128) -> u128 {
let x = if signaling {
T::snan(Some(fill))
T::snan(Some(payload))
} else {
T::qnan(Some(fill))
T::qnan(Some(payload))
};
if negative {
(-x).to_bits()
Expand All @@ -1085,23 +1118,38 @@ fn nan() {
}
}

assert_eq!(0x7fc00000, nanbits::<Single>(false, false, 0));
assert_eq!(0xffc00000, nanbits::<Single>(false, true, 0));
assert_eq!(0x7fc0ae72, nanbits::<Single>(false, false, 0xae72));
assert_eq!(0x7fffae72, nanbits::<Single>(false, false, 0xffffae72));
assert_eq!(0x7fa00000, nanbits::<Single>(true, false, 0));
assert_eq!(0xffa00000, nanbits::<Single>(true, true, 0));
assert_eq!(0x7f80ae72, nanbits::<Single>(true, false, 0xae72));
assert_eq!(0x7fbfae72, nanbits::<Single>(true, false, 0xffffae72));

assert_eq!(0x7ff8000000000000, nanbits::<Double>(false, false, 0));
assert_eq!(0xfff8000000000000, nanbits::<Double>(false, true, 0));
assert_eq!(0x7ff800000000ae72, nanbits::<Double>(false, false, 0xae72));
assert_eq!(0x7fffffffffffae72, nanbits::<Double>(false, false, 0xffffffffffffae72));
assert_eq!(0x7ff4000000000000, nanbits::<Double>(true, false, 0));
assert_eq!(0xfff4000000000000, nanbits::<Double>(true, true, 0));
assert_eq!(0x7ff000000000ae72, nanbits::<Double>(true, false, 0xae72));
assert_eq!(0x7ff7ffffffffae72, nanbits::<Double>(true, false, 0xffffffffffffae72));
let tests_single = [
// expected SNaN Neg payload
(0x7fc00000, false, false, 0x00000000),
(0xffc00000, false, true, 0x00000000),
(0x7fc0ae72, false, false, 0x0000ae72),
(0x7fffae72, false, false, 0xffffae72),
(0x7fdaae72, false, false, 0x00daae72),
(0x7fa00000, true, false, 0x00000000),
(0xffa00000, true, true, 0x00000000),
(0x7f80ae72, true, false, 0x0000ae72),
(0x7fbfae72, true, false, 0xffffae72),
(0x7f9aae72, true, false, 0x001aae72),
];
let tests_double = [
// expected SNaN Neg payload
(0x7ff8000000000000, false, false, 0x0000000000000000),
(0xfff8000000000000, false, true, 0x0000000000000000),
(0x7ff800000000ae72, false, false, 0x000000000000ae72),
(0x7fffffffffffae72, false, false, 0xffffffffffffae72),
(0x7ffdaaaaaaaaae72, false, false, 0x000daaaaaaaaae72),
(0x7ff4000000000000, true, false, 0x0000000000000000),
(0xfff4000000000000, true, true, 0x0000000000000000),
(0x7ff000000000ae72, true, false, 0x000000000000ae72),
(0x7ff7ffffffffae72, true, false, 0xffffffffffffae72),
(0x7ff1aaaaaaaaae72, true, false, 0x0001aaaaaaaaae72),
];
for (expected, signaling, negative, payload) in tests_single {
assert_eq!(expected, nanbits_from_u128::<Single>(signaling, negative, payload));
}
for (expected, signaling, negative, payload) in tests_double {
assert_eq!(expected, nanbits_from_u128::<Double>(signaling, negative, payload));
}
}

#[test]
Expand Down

0 comments on commit 3015f8f

Please sign in to comment.