diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index 4232b146fb5ac..d492903f03f6c 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -72,6 +72,7 @@ - [fd](fd.md) - [fd_read](fd-read.md) - [fixed_size_array](fixed-size-array.md) +- [float_bits_conv](float-bits-conv.md) - [float_extras](float-extras.md) - [flt2dec](flt2dec.md) - [fmt_flags_align](fmt-flags-align.md) diff --git a/src/doc/unstable-book/src/float-bits-conv.md b/src/doc/unstable-book/src/float-bits-conv.md new file mode 100644 index 0000000000000..f519545ac78b5 --- /dev/null +++ b/src/doc/unstable-book/src/float-bits-conv.md @@ -0,0 +1,7 @@ +# `float_bits_conv` + +The tracking issue for this feature is: [#40470] + +[#40470]: https://github.com/rust-lang/rust/issues/40470 + +------------------------ diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index dd8318006835f..316e6841c4fea 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -1226,6 +1226,68 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Raw transmutation to `u32`. + /// + /// Converts the `f32` into its raw memory representation, + /// similar to the `transmute` function. + /// + /// Note that this function is distinct from casting. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_bits_conv)] + /// assert_ne!((1f32).to_bits(), 1f32 as u32); // to_bits() is not casting! + /// assert_eq!((12.5f32).to_bits(), 0x41480000); + /// + /// ``` + #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] + #[inline] + pub fn to_bits(self) -> u32 { + unsafe { ::mem::transmute(self) } + } + + /// Raw transmutation from `u32`. + /// + /// Converts the given `u32` containing the float's raw memory + /// representation into the `f32` type, similar to the + /// `transmute` function. + /// + /// There is only one difference to a bare `transmute`: + /// Due to the implications onto Rust's safety promises being + /// uncertain, if the representation of a signaling NaN "sNaN" float + /// is passed to the function, the implementation is allowed to + /// return a quiet NaN instead. + /// + /// Note that this function is distinct from casting. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_bits_conv)] + /// use std::f32; + /// let v = f32::from_bits(0x41480000); + /// let difference = (v - 12.5).abs(); + /// assert!(difference <= 1e-5); + /// // Example for a signaling NaN value: + /// let snan = 0x7F800001; + /// assert_ne!(f32::from_bits(snan).to_bits(), snan); + /// ``` + #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] + #[inline] + 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) } + } } #[cfg(test)] @@ -1870,4 +1932,31 @@ mod tests { assert_approx_eq!(ln_2, 2f32.ln()); assert_approx_eq!(ln_10, 10f32.ln()); } + + #[test] + fn test_float_bits_conv() { + assert_eq!((1f32).to_bits(), 0x3f800000); + assert_eq!((12.5f32).to_bits(), 0x41480000); + assert_eq!((1337f32).to_bits(), 0x44a72000); + assert_eq!((-14.25f32).to_bits(), 0xc1640000); + assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); + assert_approx_eq!(f32::from_bits(0x41480000), 12.5); + 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 2f02e01935a49..be55cb80c92fa 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -1118,6 +1118,68 @@ impl f64 { } } } + + /// Raw transmutation to `u64`. + /// + /// Converts the `f64` into its raw memory representation, + /// similar to the `transmute` function. + /// + /// Note that this function is distinct from casting. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_bits_conv)] + /// assert!((1f64).to_bits() != 1f64 as u64); // to_bits() is not casting! + /// assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + /// + /// ``` + #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] + #[inline] + pub fn to_bits(self) -> u64 { + unsafe { ::mem::transmute(self) } + } + + /// Raw transmutation from `u64`. + /// + /// Converts the given `u64` containing the float's raw memory + /// representation into the `f64` type, similar to the + /// `transmute` function. + /// + /// There is only one difference to a bare `transmute`: + /// Due to the implications onto Rust's safety promises being + /// uncertain, if the representation of a signaling NaN "sNaN" float + /// is passed to the function, the implementation is allowed to + /// return a quiet NaN instead. + /// + /// Note that this function is distinct from casting. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_bits_conv)] + /// use std::f64; + /// let v = f64::from_bits(0x4029000000000000); + /// let difference = (v - 12.5).abs(); + /// assert!(difference <= 1e-5); + /// // Example for a signaling NaN value: + /// let snan = 0x7FF0000000000001; + /// assert_ne!(f64::from_bits(snan).to_bits(), snan); + /// ``` + #[unstable(feature = "float_bits_conv", reason = "recently added", issue = "40470")] + #[inline] + 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) } + } } #[cfg(test)] @@ -1755,4 +1817,16 @@ mod tests { assert_approx_eq!(ln_2, 2f64.ln()); assert_approx_eq!(ln_10, 10f64.ln()); } + + #[test] + fn test_float_bits_conv() { + assert_eq!((1f64).to_bits(), 0x3ff0000000000000); + assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + assert_eq!((1337f64).to_bits(), 0x4094e40000000000); + assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); + assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); + assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 4f6d170560c1d..8de6e1a24f1f2 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -321,6 +321,7 @@ #![feature(zero_one)] #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(stage0, feature(pub_restricted))] +#![cfg_attr(test, feature(float_bits_conv))] // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std.