diff options
| author | bors <bors@rust-lang.org> | 2017-04-18 11:23:44 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-04-18 11:23:44 +0000 |
| commit | c398efc53f09f6e1a8cba4ec2259ffb9d89f0542 (patch) | |
| tree | 17060193776655170f41a7489ff173152172f1eb /src/libstd | |
| parent | e621e1c73f1487b4450446eafe6ccad3a99e3cd8 (diff) | |
| parent | 0c148153f4de0c32206582ed9b51346f9769f10c (diff) | |
| download | rust-c398efc53f09f6e1a8cba4ec2259ffb9d89f0542.tar.gz rust-c398efc53f09f6e1a8cba4ec2259ffb9d89f0542.zip | |
Auto merge of #39271 - est31:add_float_bits, r=BurntSushi
Add functions to safely transmute float to int The safe subset of Rust tries to be as powerful as possible. While it is very powerful already, its currently impossible to safely transmute integers to floats. While crates exist that provide a safe interface, most prominently the `iee754` crate (which also inspired naming of the added functions), they themselves only use the unsafe `mem::transmute` function to accomplish this task. Also, including an entire crate for just two lines of unsafe code seems quite wasteful. That's why this PR adds functions to safely transmute integers to floats and vice versa, currently gated by the newly added `float_bits_conv` feature. The functions added are no niche case. Not just `ieee754` [currently implements](https://github.com/huonw/ieee754/blob/master/src/lib.rs#L441) float to int transmutation via unsafe code but also the [very popular `byteorder` crate](https://github.com/BurntSushi/byteorder/blob/1.0.0/src/lib.rs#L258). This functionality of byteorder is in turn used by higher level crates. I only give two examples out of many: [chor](https://github.com/pyfisch/cbor/blob/a7363ea9aaf372e3d24b52414b5c76552ecc91c8/src/ser.rs#L227) and [bincode](https://github.com/TyOverby/bincode/blob/f06a4cfcb5b194e54d4997c200c75b88b6c3fba4/src/serde/reader.rs#L218). One alternative would be to manually use functions like pow or multiplication by 1 to get a similar result, but they only work in the int -> float direction, and are not bit exact, and much slower (also, most likely the optimizer will never optimize it to a transmute because the conversion is not bit exact while the transmute is). Tracking issue: #40470
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/f32.rs | 89 | ||||
| -rw-r--r-- | src/libstd/f64.rs | 74 | ||||
| -rw-r--r-- | src/libstd/lib.rs | 1 |
3 files changed, 164 insertions, 0 deletions
diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index dd831800683..316e6841c4f 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 2f02e01935a..be55cb80c92 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 4f6d170560c..8de6e1a24f1 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. |
