@@ -998,10 +998,13 @@ impl f32 {
998998
999999 /// Raw transmutation to `u32`.
10001000 ///
1001- /// Converts the `f32` into its raw memory representation,
1002- /// similar to the `transmute` function.
1001+ /// This is currently identical to `transmute::<f32, u32>(self)` on all platforms.
10031002 ///
1004- /// Note that this function is distinct from casting.
1003+ /// See `from_bits` for some discussion of the portability of this operation
1004+ /// (there are almost no issues).
1005+ ///
1006+ /// Note that this function is distinct from `as` casting, which attempts to
1007+ /// preserve the *numeric* value, and not the bitwise value.
10051008 ///
10061009 /// # Examples
10071010 ///
@@ -1018,17 +1021,33 @@ impl f32 {
10181021
10191022 /// Raw transmutation from `u32`.
10201023 ///
1021- /// Converts the given `u32` containing the float's raw memory
1022- /// representation into the `f32` type, similar to the
1023- /// `transmute` function.
1024+ /// This is currently identical to `transmute::<u32, f32>(v)` on all platforms.
1025+ /// It turns out this is incredibly portable, for two reasons:
1026+ ///
1027+ /// * Floats and Ints have the same endianess on all supported platforms.
1028+ /// * IEEE-754 very precisely specifies the bit layout of floats.
1029+ ///
1030+ /// However there is one caveat: prior to the 2008 version of IEEE-754, how
1031+ /// to interpret the NaN signaling bit wasn't actually specified. Most platforms
1032+ /// (notably x86 and ARM) picked the interpretation that was ultimately
1033+ /// standardized in 2008, but some didn't (notably MIPS). As a result, all
1034+ /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa.
1035+ ///
1036+ /// Rather than trying to preserve signaling-ness cross-platform, this
1037+ /// implementation favours preserving the exact bits. This means that
1038+ /// any payloads encoded in NaNs will be preserved even if the result of
1039+ /// this method is sent over the network from an x86 machine to a MIPS one.
1040+ ///
1041+ /// If the results of this method are only manipulated by the same
1042+ /// architecture that produced them, then there is no portability concern.
1043+ ///
1044+ /// If the input isn't NaN, then there is no portability concern.
10241045 ///
1025- /// There is only one difference to a bare `transmute`:
1026- /// Due to the implications onto Rust's safety promises being
1027- /// uncertain, if the representation of a signaling NaN "sNaN" float
1028- /// is passed to the function, the implementation is allowed to
1029- /// return a quiet NaN instead.
1046+ /// If you don't care about signalingness (very likely), then there is no
1047+ /// portability concern.
10301048 ///
1031- /// Note that this function is distinct from casting.
1049+ /// Note that this function is distinct from `as` casting, which attempts to
1050+ /// preserve the *numeric* value, and not the bitwise value.
10321051 ///
10331052 /// # Examples
10341053 ///
@@ -1037,25 +1056,11 @@ impl f32 {
10371056 /// let v = f32::from_bits(0x41480000);
10381057 /// let difference = (v - 12.5).abs();
10391058 /// assert!(difference <= 1e-5);
1040- /// // Example for a signaling NaN value:
1041- /// let snan = 0x7F800001;
1042- /// assert_ne!(f32::from_bits(snan).to_bits(), snan);
10431059 /// ```
10441060 #[ stable( feature = "float_bits_conv" , since = "1.20.0" ) ]
10451061 #[ inline]
1046- pub fn from_bits ( mut v : u32 ) -> Self {
1047- const EXP_MASK : u32 = 0x7F800000 ;
1048- const FRACT_MASK : u32 = 0x007FFFFF ;
1049- if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
1050- // While IEEE 754-2008 specifies encodings for quiet NaNs
1051- // and signaling ones, certain MIPS and PA-RISC
1052- // CPUs treat signaling NaNs differently.
1053- // Therefore to be safe, we pass a known quiet NaN
1054- // if v is any kind of NaN.
1055- // The check above only assumes IEEE 754-1985 to be
1056- // valid.
1057- v = unsafe { :: mem:: transmute ( NAN ) } ;
1058- }
1062+ pub fn from_bits ( v : u32 ) -> Self {
1063+ // It turns out the safety issues with sNaN were overblown! Hooray!
10591064 unsafe { :: mem:: transmute ( v) }
10601065 }
10611066}
@@ -1646,25 +1651,15 @@ mod tests {
16461651 assert_approx_eq ! ( f32 :: from_bits( 0x41480000 ) , 12.5 ) ;
16471652 assert_approx_eq ! ( f32 :: from_bits( 0x44a72000 ) , 1337.0 ) ;
16481653 assert_approx_eq ! ( f32 :: from_bits( 0xc1640000 ) , -14.25 ) ;
1649- }
1650- #[ test]
1651- fn test_snan_masking ( ) {
1652- // NOTE: this test assumes that our current platform
1653- // implements IEEE 754-2008 that specifies the difference
1654- // in encoding of quiet and signaling NaNs.
1655- // If you are porting Rust to a platform that does not
1656- // implement IEEE 754-2008 (but e.g. IEEE 754-1985, which
1657- // only says that "Signaling NaNs shall be reserved operands"
1658- // but doesn't specify the actual setup), feel free to
1659- // cfg out this test.
1660- let snan: u32 = 0x7F801337 ;
1661- const QNAN_MASK : u32 = 0x00400000 ;
1662- let nan_masked_fl = f32:: from_bits ( snan) ;
1663- let nan_masked = nan_masked_fl. to_bits ( ) ;
1664- // Ensure that signaling NaNs don't stay the same
1665- assert_ne ! ( nan_masked, snan) ;
1666- // Ensure that we have a quiet NaN
1667- assert_ne ! ( nan_masked & QNAN_MASK , 0 ) ;
1668- assert ! ( nan_masked_fl. is_nan( ) ) ;
1654+
1655+ // Check that NaNs roundtrip their bits regardless of signalingness
1656+ // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
1657+ let masked_nan1 = f32:: NAN . to_bits ( ) ^ 0x002A_AAAA ;
1658+ let masked_nan2 = f32:: NAN . to_bits ( ) ^ 0x0055_5555 ;
1659+ assert ! ( f32 :: from_bits( masked_nan1) . is_nan( ) ) ;
1660+ assert ! ( f32 :: from_bits( masked_nan2) . is_nan( ) ) ;
1661+
1662+ assert_eq ! ( f32 :: from_bits( masked_nan1) . to_bits( ) , masked_nan1) ;
1663+ assert_eq ! ( f32 :: from_bits( masked_nan2) . to_bits( ) , masked_nan2) ;
16691664 }
16701665}
0 commit comments