Skip to content
Open
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
2 changes: 2 additions & 0 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ use crate::marker::PointeeSized;

mod num;

#[unstable(feature = "float_conversions", issue = "159913")]
pub use num::FloatToFloat;
#[unstable(feature = "convert_float_to_int", issue = "67057")]
pub use num::FloatToInt;
#[unstable(feature = "integer_casts", issue = "157388")]
Expand Down
54 changes: 54 additions & 0 deletions library/core/src/convert/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ pub impl(self) trait FloatToInt<Int>: Sized {
#[unstable(feature = "convert_float_to_int", issue = "67057")]
#[doc(hidden)]
unsafe fn to_int_unchecked(self) -> Int;

#[unstable(feature = "float_conversions", issue = "159913")]
#[doc(hidden)]
fn to_int_saturating(self) -> Int;

#[unstable(feature = "float_conversions", issue = "159913")]
#[doc(hidden)]
fn to_int_checked(self) -> Option<Int>;
}

macro_rules! impl_float_to_int {
Expand All @@ -19,6 +27,24 @@ macro_rules! impl_float_to_int {
// SAFETY: the safety contract must be upheld by the caller.
unsafe { crate::intrinsics::float_to_int_unchecked(self) }
}
#[inline]
fn to_int_saturating(self) -> $Int {
// `as` already saturates and maps `NaN` to zero.
self as $Int
}
#[inline]
fn to_int_checked(self) -> Option<$Int> {
// `as` truncates toward zero and these bounds are exact for
// that: `MAX + 1` rounds up to the first out-of-range value,
// and the `- MIN` offset keeps the low comparison exact even
// when `MIN - 1` is not representable. `NaN` and infinities
// fail both comparisons.
if self - (<$Int>::MIN as $Float) > -1.0 && self < <$Int>::MAX as $Float + 1.0 {
Some(self as $Int)
} else {
None
}
}
}
)+
}
Expand All @@ -29,6 +55,34 @@ impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i12
impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);

/// Supporting trait for the inherent `cast` method converting between float types.
/// Typically doesn’t need to be used directly.
#[unstable(feature = "float_conversions", issue = "159913")]
pub impl(self) trait FloatToFloat<Flt>: Sized {
#[unstable(feature = "float_conversions", issue = "159913")]
#[doc(hidden)]
fn cast(self) -> Flt;
}

macro_rules! impl_float_to_float {
($Float:ty => $($Flt:ty),+) => {
$(
#[unstable(feature = "float_conversions", issue = "159913")]
impl FloatToFloat<$Flt> for $Float {
#[inline]
fn cast(self) -> $Flt {
self as $Flt
}
}
)+
}
}

impl_float_to_float!(f16 => f16, f32, f64, f128);
impl_float_to_float!(f32 => f16, f32, f64, f128);
impl_float_to_float!(f64 => f16, f32, f64, f128);
impl_float_to_float!(f128 => f16, f32, f64, f128);

/// Implement `From<bool>` for integers
macro_rules! impl_from_bool {
($($int:ty)*) => {$(
Expand Down
96 changes: 95 additions & 1 deletion library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#![unstable(feature = "f128", issue = "116909")]

use crate::convert::FloatToInt;
use crate::convert::{FloatToFloat, FloatToInt};
use crate::num::FpCategory;
use crate::panic::const_assert;
use crate::{intrinsics, mem};
Expand Down Expand Up @@ -1028,6 +1028,100 @@ impl f128 {
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
}

/// Converts to the target float type, rounding as defined in IEEE 754.
///
/// This is equivalent to `self as Flt`. Narrowing to a smaller type can
/// produce an infinity.
///
/// ```
/// #![feature(float_conversions, f128)]
/// # #[cfg(target_has_reliable_f128)] {
///
/// let x = 1.5_f128;
/// assert_eq!(x.cast::<f64>(), 1.5_f64);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn cast<Flt>(self) -> Flt
where
Self: FloatToFloat<Flt>,
{
FloatToFloat::<Flt>::cast(self)
}

/// Rounds toward zero and converts to any primitive integer type, saturating
/// at the type's boundaries and mapping `NaN` to zero.
///
/// This is equivalent to `self as Int`.
///
/// ```
/// #![feature(float_conversions, f128)]
/// # #[cfg(target_has_reliable_f128)] {
///
/// assert_eq!(4.6_f128.to_int_saturating::<u8>(), 4);
/// assert_eq!(f128::NAN.to_int_saturating::<u8>(), 0);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn to_int_saturating<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
FloatToInt::<Int>::to_int_saturating(self)
}

/// Rounds toward zero and converts to any primitive integer type, returning
/// `None` if the value is `NaN`, infinite, or does not fit in the target type.
///
/// ```
/// #![feature(float_conversions, f128)]
/// # #[cfg(target_has_reliable_f128)] {
///
/// assert_eq!(4.6_f128.to_int_checked::<u8>(), Some(4));
/// assert_eq!(f128::NAN.to_int_checked::<u8>(), None);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn to_int_checked<Int>(self) -> Option<Int>
where
Self: FloatToInt<Int>,
{
FloatToInt::<Int>::to_int_checked(self)
}

/// Rounds toward zero and converts to any primitive integer type.
///
/// This is equivalent to `self.to_int_checked().unwrap()`.
///
/// # Panics
///
/// Panics if the value is `NaN`, infinite, or does not fit in the target type.
///
/// ```
/// #![feature(float_conversions, f128)]
/// # #[cfg(target_has_reliable_f128)] {
///
/// assert_eq!(4.6_f128.to_int_strict::<u8>(), 4);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
#[track_caller]
pub fn to_int_strict<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
self.to_int_checked::<Int>()
.expect("the value cannot be represented in the target integer type")
}

/// Raw transmutation to `u128`.
///
/// This is currently identical to `transmute::<f128, u128>(self)` on all platforms.
Expand Down
96 changes: 95 additions & 1 deletion library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#![unstable(feature = "f16", issue = "116909")]

use crate::convert::FloatToInt;
use crate::convert::{FloatToFloat, FloatToInt};
use crate::num::FpCategory;
#[cfg(not(test))]
use crate::num::imp::libm;
Expand Down Expand Up @@ -1024,6 +1024,100 @@ impl f16 {
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
}

/// Converts to the target float type, rounding as defined in IEEE 754.
///
/// This is equivalent to `self as Flt`. Narrowing to a smaller type can
/// produce an infinity.
///
/// ```
/// #![feature(float_conversions, f16)]
/// # #[cfg(target_has_reliable_f16)] {
///
/// let x = 1.5_f16;
/// assert_eq!(x.cast::<f32>(), 1.5_f32);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn cast<Flt>(self) -> Flt
where
Self: FloatToFloat<Flt>,
{
FloatToFloat::<Flt>::cast(self)
}

/// Rounds toward zero and converts to any primitive integer type, saturating
/// at the type's boundaries and mapping `NaN` to zero.
///
/// This is equivalent to `self as Int`.
///
/// ```
/// #![feature(float_conversions, f16)]
/// # #[cfg(target_has_reliable_f16)] {
///
/// assert_eq!(4.6_f16.to_int_saturating::<u8>(), 4);
/// assert_eq!(f16::NAN.to_int_saturating::<u8>(), 0);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn to_int_saturating<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
FloatToInt::<Int>::to_int_saturating(self)
}

/// Rounds toward zero and converts to any primitive integer type, returning
/// `None` if the value is `NaN`, infinite, or does not fit in the target type.
///
/// ```
/// #![feature(float_conversions, f16)]
/// # #[cfg(target_has_reliable_f16)] {
///
/// assert_eq!(4.6_f16.to_int_checked::<u8>(), Some(4));
/// assert_eq!(f16::NAN.to_int_checked::<u8>(), None);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub fn to_int_checked<Int>(self) -> Option<Int>
where
Self: FloatToInt<Int>,
{
FloatToInt::<Int>::to_int_checked(self)
}

/// Rounds toward zero and converts to any primitive integer type.
///
/// This is equivalent to `self.to_int_checked().unwrap()`.
///
/// # Panics
///
/// Panics if the value is `NaN`, infinite, or does not fit in the target type.
///
/// ```
/// #![feature(float_conversions, f16)]
/// # #[cfg(target_has_reliable_f16)] {
///
/// assert_eq!(4.6_f16.to_int_strict::<u8>(), 4);
/// # }
/// ```
#[unstable(feature = "float_conversions", issue = "159913")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
#[track_caller]
pub fn to_int_strict<Int>(self) -> Int
where
Self: FloatToInt<Int>,
{
self.to_int_checked::<Int>()
.expect("the value cannot be represented in the target integer type")
}

/// Raw transmutation to `u16`.
///
/// This is currently identical to `transmute::<f16, u16>(self)` on all platforms.
Expand Down
Loading
Loading