Skip to content

Commit 3ba0f07

Browse files
committed
Make sNaN removal code tolerate different sNaN encodings
IEEE 754-1985 specifies the encoding of NaN floating point numbers, but while it mentions that NaNs can be subdivided into signaling and quiet ones, it doesn't fix the encoding of signaling NaNs in binary formats. This led to different implementations (CPUs) having different encodings. IEEE 754-2008 finally specified the encoding of signaling NaNs but some architectures are compatible with it, while others aren't. Certain MIPS and PA-RISC CPUs have different encodings for signaling NaNs. In order to have the float <-> binary cast feature of the std library be portable to them, we don't mask any quiet NaNs like we did before (only being compliant to IEEE 754-2008 and nothing else), but instead we simply pass a known good NaN instead. Note that in the code removed there was a bug; the 64 bit mask for quiet NaNs should have been `0x0008000000000000` instead of the specified `0x0001000000000000`.
1 parent 1d2db7b commit 3ba0f07

File tree

2 files changed

+24
-13
lines changed

2 files changed

+24
-13
lines changed

src/libstd/f32.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -1131,13 +1131,16 @@ impl f32 {
11311131
#[inline]
11321132
pub fn from_bits(mut v: u32) -> Self {
11331133
const EXP_MASK: u32 = 0x7F800000;
1134-
const QNAN_MASK: u32 = 0x00400000;
11351134
const FRACT_MASK: u32 = 0x007FFFFF;
11361135
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
1137-
// If we have a NaN value, we
1138-
// convert signaling NaN values to quiet NaN
1139-
// by setting the the highest bit of the fraction
1140-
v |= QNAN_MASK;
1136+
// While IEEE 754-2008 specifies encodings for quiet NaNs
1137+
// and signaling ones, certain MIPS and PA-RISC
1138+
// CPUs treat signaling NaNs differently.
1139+
// Therefore to be safe, we pass a known quiet NaN
1140+
// if v is any kind of NaN.
1141+
// The check above only assumes IEEE 754-1985 to be
1142+
// valid.
1143+
v = unsafe { ::mem::transmute(NAN) };
11411144
}
11421145
unsafe { ::mem::transmute(v) }
11431146
}
@@ -1732,8 +1735,15 @@ mod tests {
17321735
}
17331736
#[test]
17341737
fn test_snan_masking() {
1738+
// NOTE: this test assumes that our current platform
1739+
// implements IEEE 754-2008 that specifies the difference
1740+
// in encoding of quiet and signaling NaNs.
1741+
// If you are porting Rust to a platform that does not
1742+
// implement IEEE 754-2008 (but e.g. IEEE 754-1985, which
1743+
// only says that "Signaling NaNs shall be reserved operands"
1744+
// but doesn't specify the actual setup), feel free to
1745+
// cfg out this test.
17351746
let snan: u32 = 0x7F801337;
1736-
const PAYLOAD_MASK: u32 = 0x003FFFFF;
17371747
const QNAN_MASK: u32 = 0x00400000;
17381748
let nan_masked_fl = f32::from_bits(snan);
17391749
let nan_masked = nan_masked_fl.to_bits();
@@ -1742,7 +1752,5 @@ mod tests {
17421752
// Ensure that we have a quiet NaN
17431753
assert_ne!(nan_masked & QNAN_MASK, 0);
17441754
assert!(nan_masked_fl.is_nan());
1745-
// Ensure the payload wasn't touched during conversion
1746-
assert_eq!(nan_masked & PAYLOAD_MASK, snan & PAYLOAD_MASK);
17471755
}
17481756
}

src/libstd/f64.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1046,13 +1046,16 @@ impl f64 {
10461046
#[inline]
10471047
pub fn from_bits(mut v: u64) -> Self {
10481048
const EXP_MASK: u64 = 0x7FF0000000000000;
1049-
const QNAN_MASK: u64 = 0x0001000000000000;
10501049
const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF;
10511050
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
1052-
// If we have a NaN value, we
1053-
// convert signaling NaN values to quiet NaN
1054-
// by setting the the highest bit of the fraction
1055-
v |= QNAN_MASK;
1051+
// While IEEE 754-2008 specifies encodings for quiet NaNs
1052+
// and signaling ones, certain MIPS and PA-RISC
1053+
// CPUs treat signaling NaNs differently.
1054+
// Therefore to be safe, we pass a known quiet NaN
1055+
// if v is any kind of NaN.
1056+
// The check above only assumes IEEE 754-1985 to be
1057+
// valid.
1058+
v = unsafe { ::mem::transmute(NAN) };
10561059
}
10571060
unsafe { ::mem::transmute(v) }
10581061
}

0 commit comments

Comments
 (0)