Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/doc/unstable-book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions src/doc/unstable-book/src/float-bits-conv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `float_bits_conv`

The tracking issue for this feature is: [#40470]

[#40470]: https://github.com/rust-lang/rust/issues/40470

------------------------
89 changes: 89 additions & 0 deletions src/libstd/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
}
}
74 changes: 74 additions & 0 deletions src/libstd/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
}
}
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down