Skip to content

Commit 3993eb4

Browse files
committed
Preserve sNaN payload when converting them to quiet NaNs
1 parent fc028b8 commit 3993eb4

File tree

2 files changed

+35
-14
lines changed

2 files changed

+35
-14
lines changed

src/libstd/f32.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -1276,14 +1276,17 @@ impl f32 {
12761276
/// ```
12771277
#[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")]
12781278
#[inline]
1279-
pub fn from_bits(v: u32) -> Self {
1280-
match v {
1281-
// sNaN limits source:
1282-
// https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html
1283-
0x7F800001 ... 0x7FBFFFFF |
1284-
0xFF800001 ... 0xFFBFFFFF => ::f32::NAN,
1285-
_ => unsafe { ::mem::transmute(v) },
1279+
pub fn from_bits(mut v: u32) -> Self {
1280+
const EXP_MASK: u32 = 0x7F800000;
1281+
const QNAN_MASK: u32 = 0x00400000;
1282+
const FRACT_MASK: u32 = 0x007FFFFF;
1283+
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
1284+
// If we have a NaN value, we
1285+
// convert signaling NaN values to quiet NaN
1286+
// by setting the the highest bit of the fraction
1287+
v |= QNAN_MASK;
12861288
}
1289+
unsafe { ::mem::transmute(v) }
12871290
}
12881291
}
12891292

@@ -1941,4 +1944,19 @@ mod tests {
19411944
assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0);
19421945
assert_approx_eq!(f32::from_bits(0xc1640000), -14.25);
19431946
}
1947+
#[test]
1948+
fn test_snan_masking() {
1949+
let snan: u32 = 0x7F801337;
1950+
const PAYLOAD_MASK: u32 = 0x003FFFFF;
1951+
const QNAN_MASK: u32 = 0x00400000;
1952+
let nan_masked_fl = f32::from_bits(snan);
1953+
let nan_masked = nan_masked_fl.to_bits();
1954+
// Ensure that signaling NaNs don't stay the same
1955+
assert_ne!(nan_masked, snan);
1956+
// Ensure that we have a quiet NaN
1957+
assert_ne!(nan_masked & QNAN_MASK, 0);
1958+
assert!(nan_masked_fl.is_nan());
1959+
// Ensure the payload wasn't touched during conversion
1960+
assert_eq!(nan_masked & PAYLOAD_MASK, snan & PAYLOAD_MASK);
1961+
}
19441962
}

src/libstd/f64.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1168,14 +1168,17 @@ impl f64 {
11681168
/// ```
11691169
#[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")]
11701170
#[inline]
1171-
pub fn from_bits(v: u64) -> Self {
1172-
match v {
1173-
// sNaN limits source:
1174-
// https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html
1175-
0x7FF0000000000001 ... 0x7FF7FFFFFFFFFFFF |
1176-
0xFFF0000000000001 ... 0xFFF7FFFFFFFFFFFF => ::f64::NAN,
1177-
_ => unsafe { ::mem::transmute(v) },
1171+
pub fn from_bits(mut v: u64) -> Self {
1172+
const EXP_MASK: u64 = 0x7FF0000000000000;
1173+
const QNAN_MASK: u64 = 0x0001000000000000;
1174+
const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF;
1175+
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
1176+
// If we have a NaN value, we
1177+
// convert signaling NaN values to quiet NaN
1178+
// by setting the the highest bit of the fraction
1179+
v |= QNAN_MASK;
11781180
}
1181+
unsafe { ::mem::transmute(v) }
11791182
}
11801183
}
11811184

0 commit comments

Comments
 (0)