Add {f32,f64}::approx_unchecked_to<Int> unsafe methods
As discussed in https://github.com/rust-lang/rust/issues/10184 Currently, casting a floating point number to an integer with `as` is Undefined Behavior if the value is out of range. `-Z saturating-float-casts` fixes this soundness hole by making `as` “saturate” to the maximum or minimum value of the integer type (or zero for `NaN`), but has measurable negative performance impact in some benchmarks. There is some consensus in that thread for enabling saturation by default anyway, but provide an `unsafe fn` alternative for users who know through some other mean that their values are in range.
This commit is contained in:
@@ -40,6 +40,11 @@
|
|||||||
|
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
||||||
|
mod num;
|
||||||
|
|
||||||
|
#[unstable(feature = "convert_float_to_int", issue = "67057")]
|
||||||
|
pub use num::FloatToInt;
|
||||||
|
|
||||||
/// The identity function.
|
/// The identity function.
|
||||||
///
|
///
|
||||||
/// Two things are important to note about this function:
|
/// Two things are important to note about this function:
|
||||||
|
|||||||
38
src/libcore/convert/num.rs
Normal file
38
src/libcore/convert/num.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
mod private {
|
||||||
|
/// This trait being unreachable from outside the crate
|
||||||
|
/// prevents other implementations of the `FloatToInt` trait,
|
||||||
|
/// which allows potentially adding more trait methods after the trait is `#[stable]`.
|
||||||
|
#[unstable(feature = "convert_float_to_int", issue = "67057")]
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supporting trait for inherent methods of `f32` and `f64` such as `round_unchecked_to`.
|
||||||
|
/// Typically doesn’t need to be used directly.
|
||||||
|
#[unstable(feature = "convert_float_to_int", issue = "67057")]
|
||||||
|
pub trait FloatToInt<Int>: private::Sealed + Sized {
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[unstable(feature = "float_approx_unchecked_to", issue = "67058")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
unsafe fn approx_unchecked(self) -> Int;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_float_to_int {
|
||||||
|
( $Float: ident => $( $Int: ident )+ ) => {
|
||||||
|
#[unstable(feature = "convert_float_to_int", issue = "67057")]
|
||||||
|
impl private::Sealed for $Float {}
|
||||||
|
$(
|
||||||
|
#[unstable(feature = "convert_float_to_int", issue = "67057")]
|
||||||
|
impl FloatToInt<$Int> for $Float {
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn approx_unchecked(self) -> $Int {
|
||||||
|
crate::intrinsics::float_to_int_approx_unchecked(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_float_to_int!(f32 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
|
||||||
|
impl_float_to_int!(f64 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
|
||||||
@@ -1144,6 +1144,11 @@ extern "rust-intrinsic" {
|
|||||||
/// May assume inputs are finite.
|
/// May assume inputs are finite.
|
||||||
pub fn frem_fast<T>(a: T, b: T) -> T;
|
pub fn frem_fast<T>(a: T, b: T) -> T;
|
||||||
|
|
||||||
|
/// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range
|
||||||
|
/// https://github.com/rust-lang/rust/issues/10184
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
pub fn float_to_int_approx_unchecked<Float, Int>(value: Float) -> Int;
|
||||||
|
|
||||||
|
|
||||||
/// Returns the number of bits set in an integer type `T`
|
/// Returns the number of bits set in an integer type `T`
|
||||||
pub fn ctpop<T>(x: T) -> T;
|
pub fn ctpop<T>(x: T) -> T;
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
|
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
use crate::convert::FloatToInt;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use crate::intrinsics;
|
use crate::intrinsics;
|
||||||
|
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::num::FpCategory;
|
use crate::num::FpCategory;
|
||||||
|
|
||||||
@@ -400,6 +401,35 @@ impl f32 {
|
|||||||
intrinsics::minnumf32(self, other)
|
intrinsics::minnumf32(self, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rounds toward zero and converts to any primitive integer type,
|
||||||
|
/// assuming that the value is finite and fits in that type.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(float_approx_unchecked_to)]
|
||||||
|
///
|
||||||
|
/// let value = 4.6_f32;
|
||||||
|
/// let rounded = unsafe { value.approx_unchecked_to::<u16>() };
|
||||||
|
/// assert_eq!(rounded, 4);
|
||||||
|
///
|
||||||
|
/// let value = -128.9_f32;
|
||||||
|
/// let rounded = unsafe { value.approx_unchecked_to::<i8>() };
|
||||||
|
/// assert_eq!(rounded, std::i8::MIN);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The value must:
|
||||||
|
///
|
||||||
|
/// * Not be `NaN`
|
||||||
|
/// * Not be infinite
|
||||||
|
/// * Be representable in the return type `Int`, after truncating off its fractional part
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[unstable(feature = "float_approx_unchecked_to", issue = "67058")]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn approx_unchecked_to<Int>(self) -> Int where Self: FloatToInt<Int> {
|
||||||
|
FloatToInt::<Int>::approx_unchecked(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Raw transmutation to `u32`.
|
/// Raw transmutation to `u32`.
|
||||||
///
|
///
|
||||||
/// This is currently identical to `transmute::<f32, u32>(self)` on all platforms.
|
/// This is currently identical to `transmute::<f32, u32>(self)` on all platforms.
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
|
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
use crate::convert::FloatToInt;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use crate::intrinsics;
|
use crate::intrinsics;
|
||||||
|
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::num::FpCategory;
|
use crate::num::FpCategory;
|
||||||
|
|
||||||
@@ -413,6 +414,35 @@ impl f64 {
|
|||||||
intrinsics::minnumf64(self, other)
|
intrinsics::minnumf64(self, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rounds toward zero and converts to any primitive integer type,
|
||||||
|
/// assuming that the value is finite and fits in that type.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(float_approx_unchecked_to)]
|
||||||
|
///
|
||||||
|
/// let value = 4.6_f32;
|
||||||
|
/// let rounded = unsafe { value.approx_unchecked_to::<u16>() };
|
||||||
|
/// assert_eq!(rounded, 4);
|
||||||
|
///
|
||||||
|
/// let value = -128.9_f32;
|
||||||
|
/// let rounded = unsafe { value.approx_unchecked_to::<i8>() };
|
||||||
|
/// assert_eq!(rounded, std::i8::MIN);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The value must:
|
||||||
|
///
|
||||||
|
/// * Not be `NaN`
|
||||||
|
/// * Not be infinite
|
||||||
|
/// * Be representable in the return type `Int`, after truncating off its fractional part
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[unstable(feature = "float_approx_unchecked_to", issue = "67058")]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn approx_unchecked_to<Int>(self) -> Int where Self: FloatToInt<Int> {
|
||||||
|
FloatToInt::<Int>::approx_unchecked(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Raw transmutation to `u64`.
|
/// Raw transmutation to `u64`.
|
||||||
///
|
///
|
||||||
/// This is currently identical to `transmute::<f64, u64>(self)` on all platforms.
|
/// This is currently identical to `transmute::<f64, u64>(self)` on all platforms.
|
||||||
|
|||||||
@@ -516,9 +516,36 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"float_to_int_approx_unchecked" => {
|
||||||
|
if float_type_width(arg_tys[0]).is_none() {
|
||||||
|
span_invalid_monomorphization_error(
|
||||||
|
tcx.sess, span,
|
||||||
|
&format!("invalid monomorphization of `float_to_int_approx_unchecked` \
|
||||||
|
intrinsic: expected basic float type, \
|
||||||
|
found `{}`", arg_tys[0]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match int_type_width_signed(ret_ty, self.cx) {
|
||||||
|
Some((width, signed)) => {
|
||||||
|
if signed {
|
||||||
|
self.fptosi(args[0].immediate(), self.cx.type_ix(width))
|
||||||
|
} else {
|
||||||
|
self.fptoui(args[0].immediate(), self.cx.type_ix(width))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
span_invalid_monomorphization_error(
|
||||||
|
tcx.sess, span,
|
||||||
|
&format!("invalid monomorphization of `float_to_int_approx_unchecked` \
|
||||||
|
intrinsic: expected basic integer type, \
|
||||||
|
found `{}`", ret_ty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"discriminant_value" => {
|
"discriminant_value" => {
|
||||||
args[0].deref(self.cx()).codegen_get_discr(self, ret_ty)
|
args[0].deref(self.cx()).codegen_get_discr(self, ret_ty)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
|
|||||||
(1, vec![param(0), param(0)], param(0)),
|
(1, vec![param(0), param(0)], param(0)),
|
||||||
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" =>
|
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" =>
|
||||||
(1, vec![param(0), param(0)], param(0)),
|
(1, vec![param(0), param(0)], param(0)),
|
||||||
|
"float_to_int_approx_unchecked" => (2, vec![ param(0) ], param(1)),
|
||||||
|
|
||||||
"assume" => (0, vec![tcx.types.bool], tcx.mk_unit()),
|
"assume" => (0, vec![tcx.types.bool], tcx.mk_unit()),
|
||||||
"likely" => (0, vec![tcx.types.bool], tcx.types.bool),
|
"likely" => (0, vec![tcx.types.bool], tcx.types.bool),
|
||||||
|
|||||||
Reference in New Issue
Block a user