@@ -998,10 +998,13 @@ impl f32 {
998
998
999
999
/// Raw transmutation to `u32`.
1000
1000
///
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.
1003
1002
///
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.
1005
1008
///
1006
1009
/// # Examples
1007
1010
///
@@ -1018,17 +1021,33 @@ impl f32 {
1018
1021
1019
1022
/// Raw transmutation from `u32`.
1020
1023
///
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.
1024
1045
///
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.
1030
1048
///
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.
1032
1051
///
1033
1052
/// # Examples
1034
1053
///
@@ -1037,25 +1056,11 @@ impl f32 {
1037
1056
/// let v = f32::from_bits(0x41480000);
1038
1057
/// let difference = (v - 12.5).abs();
1039
1058
/// 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);
1043
1059
/// ```
1044
1060
#[ stable( feature = "float_bits_conv" , since = "1.20.0" ) ]
1045
1061
#[ 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!
1059
1064
unsafe { :: mem:: transmute ( v) }
1060
1065
}
1061
1066
}
@@ -1646,25 +1651,15 @@ mod tests {
1646
1651
assert_approx_eq ! ( f32 :: from_bits( 0x41480000 ) , 12.5 ) ;
1647
1652
assert_approx_eq ! ( f32 :: from_bits( 0x44a72000 ) , 1337.0 ) ;
1648
1653
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) ;
1669
1664
}
1670
1665
}
0 commit comments