-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Expose float from_bits and to_bits in libcore. #46931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,11 +27,10 @@ | |
//! Many functions in this module only handle normal numbers. The dec2flt routines conservatively | ||
//! take the universally-correct slow path (Algorithm M) for very small and very large numbers. | ||
//! That algorithm needs only next_float() which does handle subnormals and zeros. | ||
use u32; | ||
use cmp::Ordering::{Less, Equal, Greater}; | ||
use convert::TryInto; | ||
use ops::{Mul, Div, Neg}; | ||
use fmt::{Debug, LowerExp}; | ||
use mem::transmute; | ||
use num::diy_float::Fp; | ||
use num::FpCategory::{Infinite, Zero, Subnormal, Normal, Nan}; | ||
use num::Float; | ||
|
@@ -66,12 +65,6 @@ pub trait RawFloat : Float + Copy + Debug + LowerExp | |
/// Returns the mantissa, exponent and sign as integers. | ||
fn integer_decode(self) -> (u64, i16, i8); | ||
|
||
/// Get the raw binary representation of the float. | ||
fn transmute(self) -> u64; | ||
|
||
/// Transmute the raw binary representation into a float. | ||
fn from_bits(bits: u64) -> Self; | ||
|
||
/// Decode the float. | ||
fn unpack(self) -> Unpacked; | ||
|
||
|
@@ -159,7 +152,7 @@ impl RawFloat for f32 { | |
|
||
/// Returns the mantissa, exponent and sign as integers. | ||
fn integer_decode(self) -> (u64, i16, i8) { | ||
let bits: u32 = unsafe { transmute(self) }; | ||
let bits = self.to_bits(); | ||
let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; | ||
let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; | ||
let mantissa = if exponent == 0 { | ||
|
@@ -172,16 +165,6 @@ impl RawFloat for f32 { | |
(mantissa as u64, exponent, sign) | ||
} | ||
|
||
fn transmute(self) -> u64 { | ||
let bits: u32 = unsafe { transmute(self) }; | ||
bits as u64 | ||
} | ||
|
||
fn from_bits(bits: u64) -> f32 { | ||
assert!(bits < u32::MAX as u64, "f32::from_bits: too many bits"); | ||
unsafe { transmute(bits as u32) } | ||
} | ||
|
||
fn unpack(self) -> Unpacked { | ||
let (sig, exp, _sig) = self.integer_decode(); | ||
Unpacked::new(sig, exp) | ||
|
@@ -210,7 +193,7 @@ impl RawFloat for f64 { | |
|
||
/// Returns the mantissa, exponent and sign as integers. | ||
fn integer_decode(self) -> (u64, i16, i8) { | ||
let bits: u64 = unsafe { transmute(self) }; | ||
let bits = self.to_bits(); | ||
let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; | ||
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; | ||
let mantissa = if exponent == 0 { | ||
|
@@ -223,15 +206,6 @@ impl RawFloat for f64 { | |
(mantissa, exponent, sign) | ||
} | ||
|
||
fn transmute(self) -> u64 { | ||
let bits: u64 = unsafe { transmute(self) }; | ||
bits | ||
} | ||
|
||
fn from_bits(bits: u64) -> f64 { | ||
unsafe { transmute(bits) } | ||
} | ||
|
||
fn unpack(self) -> Unpacked { | ||
let (sig, exp, _sig) = self.integer_decode(); | ||
Unpacked::new(sig, exp) | ||
|
@@ -296,14 +270,14 @@ pub fn encode_normal<T: RawFloat>(x: Unpacked) -> T { | |
"encode_normal: exponent out of range"); | ||
// Leave sign bit at 0 ("+"), our numbers are all positive | ||
let bits = (k_enc as u64) << T::EXPLICIT_SIG_BITS | sig_enc; | ||
T::from_bits(bits) | ||
T::from_bits(bits.try_into().unwrap_or_else(|_| unreachable!())) | ||
} | ||
|
||
/// Construct a subnormal. A mantissa of 0 is allowed and constructs zero. | ||
pub fn encode_subnormal<T: RawFloat>(significand: u64) -> T { | ||
assert!(significand < T::MIN_SIG, "encode_subnormal: not actually subnormal"); | ||
// Encoded exponent is 0, the sign bit is 0, so we just have to reinterpret the bits. | ||
T::from_bits(significand) | ||
T::from_bits(significand.try_into().unwrap_or_else(|_| unreachable!())) | ||
} | ||
|
||
/// Approximate a bignum with an Fp. Rounds within 0.5 ULP with half-to-even. | ||
|
@@ -363,8 +337,7 @@ pub fn next_float<T: RawFloat>(x: T) -> T { | |
// too is exactly what we want! | ||
// Finally, f64::MAX + 1 = 7eff...f + 1 = 7ff0...0 = f64::INFINITY. | ||
Zero | Subnormal | Normal => { | ||
let bits: u64 = x.transmute(); | ||
T::from_bits(bits + 1) | ||
T::from_bits(x.to_bits() + T::Bits::from(1u8)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This technically changes the overflow behaviour on release, but I don't think it should be a problem, as this function isn't expected to ever trigger the assert that was originally in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this can't overflow. If x was the all-ones bit pattern, it'd be a NaN and we wouldn't enter this branch. |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2872,6 +2872,10 @@ pub enum FpCategory { | |
reason = "stable interface is via `impl f{32,64}` in later crates", | ||
issue = "32110")] | ||
pub trait Float: Sized { | ||
/// Type used by `to_bits` and `from_bits`. | ||
#[stable(feature = "core_float_bits", since = "1.24.0")] | ||
type Bits: ops::Add<Output = Self::Bits> + From<u8> + TryFrom<u64>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These bounds are pretty arbitrary. I guess it's okay for an internal trait, but alternatively you could move them to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made this work by duplicating the associated type in the |
||
|
||
/// Returns `true` if this value is NaN and false otherwise. | ||
#[stable(feature = "core", since = "1.6.0")] | ||
fn is_nan(self) -> bool; | ||
|
@@ -2933,6 +2937,13 @@ pub trait Float: Sized { | |
/// Returns the minimum of the two numbers. | ||
#[stable(feature = "core_float_min_max", since="1.20.0")] | ||
fn min(self, other: Self) -> Self; | ||
|
||
/// Raw transmutation to integer. | ||
#[stable(feature = "core_float_bits", since="1.24.0")] | ||
fn to_bits(self) -> Self::Bits; | ||
/// Raw transmutation from integer. | ||
#[stable(feature = "core_float_bits", since="1.24.0")] | ||
fn from_bits(v: Self::Bits) -> Self; | ||
} | ||
|
||
macro_rules! from_str_radix_int_impl { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be a step back, isn't there a way to keep this using just from_bits? Do you know if it has any performance impact?
The assertion
assert!(bits < u32::MAX as u64, "f32::from_bits: too many bits");
from the old code has been lost, but I suppose it is unreachable here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about performance impact, this is only called near the end of the slowest of slow paths. Edit: The other instance of this pattern, in
encode_normal
above, is also called in the slightly-less-slow-path, Algorithm R, but that path is still really slow in absolute terms. So I stand by my assesment.It's still ugly as hell but that ugliness will have to be somewhere unless all the dec2flt code is restructured to work with the
Bits
associated type instead of u64 (which would probably not be pretty either). Maybe it could move intoRawFloat::from_bits
? (But if so, it should gain a nicer panic message thanunreachable
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try seeing what needs to be done to use
Bits
instead ofu64
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably far too big a refactor to be worthwhile for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving the
try_into
tofrom_bits
seems more fruitful.