summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2017-03-30 01:49:06 +0200
committerest31 <MTest31@outlook.com>2017-04-18 02:43:16 +0200
commit3993eb4a276079af826c75e233e1a1d2ab93de13 (patch)
tree0183aa6b45f9f2bfc525f5fc253107967efceb7e /src/libstd
parentfc028b81803277f7552355052bb277b10a066249 (diff)
downloadrust-3993eb4a276079af826c75e233e1a1d2ab93de13.tar.gz
rust-3993eb4a276079af826c75e233e1a1d2ab93de13.zip
Preserve sNaN payload when converting them to quiet NaNs
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/f32.rs32
-rw-r--r--src/libstd/f64.rs17
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 @@ impl f32 {
     /// ```
     #[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 @@ mod tests {
         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 @@ impl f64 {
     /// ```
     #[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) }
     }
 }