Skip to content

Commit a43b30c

Browse files
authored
Rollup merge of #149671 - RalfJung:interpret-float-min-max, r=mati865
interpret: test SNaN handling of float min/max and update comments Also see #149563. I also renamed these enum variants so they are not almost identical.
2 parents 8e46521 + f040a1a commit a43b30c

File tree

3 files changed

+64
-52
lines changed

3 files changed

+64
-52
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,20 @@ pub(crate) enum MinMax {
4040
/// In particular, `-0.0` is considered smaller than `+0.0` and
4141
/// if either input is NaN, the result is NaN.
4242
Minimum,
43-
/// The IEEE-2008 `minNum` operation - see `f32::min` etc.
43+
/// The IEEE-2008 `minNum` operation with the SNaN handling of the
44+
/// IEEE-2019 `minimumNumber` operation - see `f32::min` etc.
4445
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
45-
/// and if one argument is NaN, the other one is returned.
46-
MinNum,
46+
/// and if one argument is NaN (quiet or signaling), the other one is returned.
47+
MinimumNumber,
4748
/// The IEEE-2019 `maximum` operation - see `f32::maximum` etc.
4849
/// In particular, `-0.0` is considered smaller than `+0.0` and
4950
/// if either input is NaN, the result is NaN.
5051
Maximum,
51-
/// The IEEE-2008 `maxNum` operation - see `f32::max` etc.
52+
/// The IEEE-2008 `maxNum` operation with the SNaN handling of the
53+
/// IEEE-2019 `maximumNumber` operation - see `f32::max` etc.
5254
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
53-
/// and if one argument is NaN, the other one is returned.
54-
MaxNum,
55+
/// and if one argument is NaN (quiet or signaling), the other one is returned.
56+
MaximumNumber,
5557
}
5658

5759
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
@@ -524,10 +526,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
524526
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
525527
}
526528

527-
sym::minnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MinNum, dest)?,
528-
sym::minnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MinNum, dest)?,
529-
sym::minnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MinNum, dest)?,
530-
sym::minnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MinNum, dest)?,
529+
sym::minnumf16 => {
530+
self.float_minmax_intrinsic::<Half>(args, MinMax::MinimumNumber, dest)?
531+
}
532+
sym::minnumf32 => {
533+
self.float_minmax_intrinsic::<Single>(args, MinMax::MinimumNumber, dest)?
534+
}
535+
sym::minnumf64 => {
536+
self.float_minmax_intrinsic::<Double>(args, MinMax::MinimumNumber, dest)?
537+
}
538+
sym::minnumf128 => {
539+
self.float_minmax_intrinsic::<Quad>(args, MinMax::MinimumNumber, dest)?
540+
}
531541

532542
sym::minimumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Minimum, dest)?,
533543
sym::minimumf32 => {
@@ -538,10 +548,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
538548
}
539549
sym::minimumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Minimum, dest)?,
540550

541-
sym::maxnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MaxNum, dest)?,
542-
sym::maxnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MaxNum, dest)?,
543-
sym::maxnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MaxNum, dest)?,
544-
sym::maxnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MaxNum, dest)?,
551+
sym::maxnumf16 => {
552+
self.float_minmax_intrinsic::<Half>(args, MinMax::MaximumNumber, dest)?
553+
}
554+
sym::maxnumf32 => {
555+
self.float_minmax_intrinsic::<Single>(args, MinMax::MaximumNumber, dest)?
556+
}
557+
sym::maxnumf64 => {
558+
self.float_minmax_intrinsic::<Double>(args, MinMax::MaximumNumber, dest)?
559+
}
560+
sym::maxnumf128 => {
561+
self.float_minmax_intrinsic::<Quad>(args, MinMax::MaximumNumber, dest)?
562+
}
545563

546564
sym::maximumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Maximum, dest)?,
547565
sym::maximumf32 => {
@@ -966,16 +984,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
966984
{
967985
let a: F = a.to_float()?;
968986
let b: F = b.to_float()?;
969-
let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b {
987+
let res = if matches!(op, MinMax::MinimumNumber | MinMax::MaximumNumber) && a == b {
970988
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
971989
// Let the machine decide which one to return.
972990
M::equal_float_min_max(self, a, b)
973991
} else {
974992
let result = match op {
975993
MinMax::Minimum => a.minimum(b),
976-
MinMax::MinNum => a.min(b),
994+
MinMax::MinimumNumber => a.min(b),
977995
MinMax::Maximum => a.maximum(b),
978-
MinMax::MaxNum => a.max(b),
996+
MinMax::MaximumNumber => a.max(b),
979997
};
980998
self.adjust_nan(result, &[a, b])
981999
};

compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
202202
sym::simd_le => Op::MirOp(BinOp::Le),
203203
sym::simd_gt => Op::MirOp(BinOp::Gt),
204204
sym::simd_ge => Op::MirOp(BinOp::Ge),
205-
sym::simd_fmax => Op::FMinMax(MinMax::MaxNum),
206-
sym::simd_fmin => Op::FMinMax(MinMax::MinNum),
205+
sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumber),
206+
sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumber),
207207
sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
208208
sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
209209
sym::simd_arith_offset => Op::WrappingOffset,
@@ -295,8 +295,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
295295
sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
296296
sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
297297
sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
298-
sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum),
299-
sym::simd_reduce_min => Op::MinMax(MinMax::MinNum),
298+
sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumber),
299+
sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumber),
300300
_ => unreachable!(),
301301
};
302302

@@ -320,8 +320,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
320320
} else {
321321
// Just boring integers, no NaNs to worry about.
322322
let mirop = match mmop {
323-
MinMax::MinNum | MinMax::Minimum => BinOp::Le,
324-
MinMax::MaxNum | MinMax::Maximum => BinOp::Ge,
323+
MinMax::MinimumNumber | MinMax::Minimum => BinOp::Le,
324+
MinMax::MaximumNumber | MinMax::Maximum => BinOp::Ge,
325325
};
326326
if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
327327
res

src/tools/miri/tests/pass/float.rs

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,15 @@ macro_rules! assert_approx_eq {
4848
};
4949
}
5050

51-
/// From IEEE 754 a Signaling NaN for single precision has the following representation:
52-
/// ```
53-
/// s | 1111 1111 | 0x..x
54-
/// ````
55-
/// Were at least one `x` is a 1.
56-
///
57-
/// This sNaN has the following representation and is used for testing purposes.:
58-
/// ```
59-
/// 0 | 1111111 | 01..0
60-
/// ```
61-
const SNAN_F32: f32 = f32::from_bits(0x7fa00000);
62-
63-
/// From IEEE 754 a Signaling NaN for double precision has the following representation:
64-
/// ```
65-
/// s | 1111 1111 111 | 0x..x
66-
/// ````
67-
/// Were at least one `x` is a 1.
68-
///
69-
/// This sNaN has the following representation and is used for testing purposes.:
70-
/// ```
71-
/// 0 | 1111 1111 111 | 01..0
72-
/// ```
73-
const SNAN_F64: f64 = f64::from_bits(0x7ff4000000000000);
51+
/// We turn the quiet NaN f*::NAN into a signaling one by flipping the first (most significant)
52+
/// two bits of the mantissa. For this we have to shift by `MANTISSA_DIGITS-3` because:
53+
/// we subtract 1 as the actual mantissa is 1 bit smaller, and 2 more as that's the width
54+
/// if the value we are shifting.
55+
const F16_SNAN: f16 = f16::from_bits(f16::NAN.to_bits() ^ (0b11 << (f16::MANTISSA_DIGITS - 3)));
56+
const F32_SNAN: f32 = f32::from_bits(f32::NAN.to_bits() ^ (0b11 << (f32::MANTISSA_DIGITS - 3)));
57+
const F64_SNAN: f64 = f64::from_bits(f64::NAN.to_bits() ^ (0b11 << (f64::MANTISSA_DIGITS - 3)));
58+
const F128_SNAN: f128 =
59+
f128::from_bits(f128::NAN.to_bits() ^ (0b11 << (f128::MANTISSA_DIGITS - 3)));
7460

7561
fn main() {
7662
basic();
@@ -757,6 +743,8 @@ fn ops() {
757743
assert_eq(f16::NAN.max(-9.0), -9.0);
758744
assert_eq((9.0_f16).min(f16::NAN), 9.0);
759745
assert_eq((-9.0_f16).max(f16::NAN), -9.0);
746+
assert_eq(F16_SNAN.min(9.0), 9.0);
747+
assert_eq((-9.0_f16).max(F16_SNAN), -9.0);
760748

761749
// f32 min/max
762750
assert_eq((1.0 as f32).max(-1.0), 1.0);
@@ -765,6 +753,8 @@ fn ops() {
765753
assert_eq(f32::NAN.max(-9.0), -9.0);
766754
assert_eq((9.0 as f32).min(f32::NAN), 9.0);
767755
assert_eq((-9.0 as f32).max(f32::NAN), -9.0);
756+
assert_eq(F32_SNAN.min(9.0), 9.0);
757+
assert_eq((-9.0_f32).max(F32_SNAN), -9.0);
768758

769759
// f64 min/max
770760
assert_eq((1.0 as f64).max(-1.0), 1.0);
@@ -773,6 +763,8 @@ fn ops() {
773763
assert_eq(f64::NAN.max(-9.0), -9.0);
774764
assert_eq((9.0 as f64).min(f64::NAN), 9.0);
775765
assert_eq((-9.0 as f64).max(f64::NAN), -9.0);
766+
assert_eq(F64_SNAN.min(9.0), 9.0);
767+
assert_eq((-9.0_f64).max(F64_SNAN), -9.0);
776768

777769
// f128 min/max
778770
assert_eq((1.0_f128).max(-1.0), 1.0);
@@ -781,6 +773,8 @@ fn ops() {
781773
assert_eq(f128::NAN.max(-9.0), -9.0);
782774
assert_eq((9.0_f128).min(f128::NAN), 9.0);
783775
assert_eq((-9.0_f128).max(f128::NAN), -9.0);
776+
assert_eq(F128_SNAN.min(9.0), 9.0);
777+
assert_eq((-9.0_f128).max(F128_SNAN), -9.0);
784778

785779
// f16 copysign
786780
assert_eq(3.5_f16.copysign(0.42), 3.5_f16);
@@ -1548,15 +1542,15 @@ fn test_non_determinism() {
15481542
test_operations_f128(25., 18.);
15491543

15501544
// SNaN^0 = (1 | NaN)
1551-
check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
1552-
check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
1545+
check_nondet(|| f32::powf(F32_SNAN, 0.0).is_nan());
1546+
check_nondet(|| f64::powf(F64_SNAN, 0.0).is_nan());
15531547

15541548
// 1^SNaN = (1 | NaN)
1555-
check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
1556-
check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
1549+
check_nondet(|| f32::powf(1.0, F32_SNAN).is_nan());
1550+
check_nondet(|| f64::powf(1.0, F64_SNAN).is_nan());
15571551

15581552
// same as powf (keep it consistent):
15591553
// x^SNaN = (1 | NaN)
1560-
check_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
1561-
check_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
1554+
check_nondet(|| f32::powi(F32_SNAN, 0).is_nan());
1555+
check_nondet(|| f64::powi(F64_SNAN, 0).is_nan());
15621556
}

0 commit comments

Comments
 (0)