From 3993eb4a276079af826c75e233e1a1d2ab93de13 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 30 Mar 2017 01:49:06 +0200 Subject: [PATCH] Preserve sNaN payload when converting them to quiet NaNs --- src/libstd/f32.rs | 32 +++++++++++++++++++++++++------- src/libstd/f64.rs | 17 ++++++++++------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 40bf7f17c3a..8759f103dff 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -1276,14 +1276,17 @@ pub fn to_bits(self) -> u32 { /// ``` #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] #[inline] - pub fn from_bits(v: u32) -> Self { - match v { - // sNaN limits source: - // https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html - 0x7F800001 ... 0x7FBFFFFF | - 0xFF800001 ... 0xFFBFFFFF => ::f32::NAN, - _ => unsafe { ::mem::transmute(v) }, + pub fn from_bits(mut v: u32) -> Self { + const EXP_MASK: u32 = 0x7F800000; + const QNAN_MASK: u32 = 0x00400000; + const FRACT_MASK: u32 = 0x007FFFFF; + if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { + // If we have a NaN value, we + // convert signaling NaN values to quiet NaN + // by setting the the highest bit of the fraction + v |= QNAN_MASK; } + unsafe { ::mem::transmute(v) } } } @@ -1941,4 +1944,19 @@ fn test_float_bits_conv() { assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); } + #[test] + fn test_snan_masking() { + let snan: u32 = 0x7F801337; + const PAYLOAD_MASK: u32 = 0x003FFFFF; + const QNAN_MASK: u32 = 0x00400000; + let nan_masked_fl = f32::from_bits(snan); + let nan_masked = nan_masked_fl.to_bits(); + // Ensure that signaling NaNs don't stay the same + assert_ne!(nan_masked, snan); + // Ensure that we have a quiet NaN + assert_ne!(nan_masked & QNAN_MASK, 0); + assert!(nan_masked_fl.is_nan()); + // Ensure the payload wasn't touched during conversion + assert_eq!(nan_masked & PAYLOAD_MASK, snan & PAYLOAD_MASK); + } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 80bc149b1a4..a4645c3a70f 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -1168,14 +1168,17 @@ pub fn to_bits(self) -> u64 { /// ``` #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] #[inline] - pub fn from_bits(v: u64) -> Self { - match v { - // sNaN limits source: - // https://www.doc.ic.ac.uk/~eedwards/compsys/float/nan.html - 0x7FF0000000000001 ... 0x7FF7FFFFFFFFFFFF | - 0xFFF0000000000001 ... 0xFFF7FFFFFFFFFFFF => ::f64::NAN, - _ => unsafe { ::mem::transmute(v) }, + pub fn from_bits(mut v: u64) -> Self { + const EXP_MASK: u64 = 0x7FF0000000000000; + const QNAN_MASK: u64 = 0x0001000000000000; + const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; + if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { + // If we have a NaN value, we + // convert signaling NaN values to quiet NaN + // by setting the the highest bit of the fraction + v |= QNAN_MASK; } + unsafe { ::mem::transmute(v) } } } -- 2.44.0