mirror of
https://github.com/rust-lang/rust.git
synced 2026-06-03 01:16:14 +03:00
implement float_to_int_unchecked
This commit is contained in:
+70
-2
@@ -1,9 +1,10 @@
|
||||
use std::iter;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use rustc_ast::ast::FloatTy;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_target::abi::{Align, LayoutOf};
|
||||
use rustc_apfloat::{Float, FloatConvert, Round, ieee::{Double, Single}};
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -279,6 +280,22 @@ fn call_intrinsic(
|
||||
this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
|
||||
}
|
||||
|
||||
"float_to_int_unchecked" => {
|
||||
let val = this.read_immediate(args[0])?;
|
||||
|
||||
let res = match val.layout.ty.kind {
|
||||
ty::Float(FloatTy::F32) => {
|
||||
this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?
|
||||
}
|
||||
ty::Float(FloatTy::F64) => {
|
||||
this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?
|
||||
}
|
||||
_ => bug!("`float_to_int_unchecked` called with non-float input type {:?}", val.layout.ty),
|
||||
};
|
||||
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
// Atomic operations
|
||||
#[rustfmt::skip]
|
||||
| "atomic_load"
|
||||
@@ -491,4 +508,55 @@ fn call_intrinsic(
|
||||
this.go_to_block(ret);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn float_to_int_unchecked<F>(
|
||||
&self,
|
||||
f: F,
|
||||
dest_ty: ty::Ty<'tcx>,
|
||||
) -> InterpResult<'tcx, Scalar<Tag>>
|
||||
where
|
||||
F: Float + Into<Scalar<Tag>> + FloatConvert<Single> + FloatConvert<Double>,
|
||||
{
|
||||
let this = self.eval_context_ref();
|
||||
|
||||
// Step 1: cut off the fractional part of `f`. The result of this is
|
||||
// guaranteed to be precisely representable in IEEE floats.
|
||||
let f = f.round_to_integral(Round::TowardZero).value;
|
||||
|
||||
// Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
|
||||
Ok(match dest_ty.kind {
|
||||
// Unsigned
|
||||
ty::Uint(t) => {
|
||||
let width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits());
|
||||
let res = f.to_u128(usize::try_from(width).unwrap());
|
||||
if res.status.is_empty() {
|
||||
// No status flags means there was no further rounding or other loss of precision.
|
||||
Scalar::from_uint(res.value, Size::from_bits(width))
|
||||
} else {
|
||||
// `f` was not representable in this integer type.
|
||||
throw_ub_format!(
|
||||
"`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
|
||||
f, dest_ty,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Signed
|
||||
ty::Int(t) => {
|
||||
let width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits());
|
||||
let res = f.to_i128(usize::try_from(width).unwrap());
|
||||
if res.status.is_empty() {
|
||||
// No status flags means there was no further rounding or other loss of precision.
|
||||
Scalar::from_int(res.value, Size::from_bits(width))
|
||||
} else {
|
||||
// `f` was not representable in this integer type.
|
||||
throw_ub_format!(
|
||||
"`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
|
||||
f, dest_ty,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Nothing else
|
||||
_ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +146,9 @@ fn casts() {
|
||||
test_cast::<f32, u32>(4294967040.0, 0u32.wrapping_sub(256));
|
||||
test_cast::<f32, u32>(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0);
|
||||
test_cast::<f32, u32>(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0);
|
||||
test_cast::<f32, u32>((u32::MAX-127) as f32, u32::MAX); // rounding loss
|
||||
test_cast::<f32, u32>((u32::MAX-128) as f32, u32::MAX-255); // rounding loss
|
||||
// unrepresentable casts
|
||||
assert_eq::<u32>((u32::MAX-127) as f32 as u32, u32::MAX); // rounds up and then becomes unrepresentable
|
||||
assert_eq::<u32>(4294967296.0f32 as u32, u32::MAX);
|
||||
assert_eq::<u32>(-5.0f32 as u32, 0);
|
||||
assert_eq::<u32>(f32::MAX as u32, u32::MAX);
|
||||
@@ -211,12 +211,12 @@ fn casts() {
|
||||
test_cast::<f64, u64>(0.0, 0);
|
||||
test_cast::<f64, u64>(-0.0, 0);
|
||||
test_cast::<f64, u64>(5.0, 5);
|
||||
test_cast::<f64, u64>(-5.0, 0);
|
||||
test_cast::<f64, u64>(1e16, 10000000000000000);
|
||||
test_cast::<f64, u64>((u64::MAX-1023) as f64, u64::MAX); // rounding loss
|
||||
test_cast::<f64, u64>((u64::MAX-1024) as f64, u64::MAX-2047); // rounding loss
|
||||
test_cast::<f64, u64>(9223372036854775808.0, 9223372036854775808);
|
||||
// unrepresentable casts
|
||||
assert_eq::<u64>(-5.0f64 as u64, 0);
|
||||
assert_eq::<u64>((u64::MAX-1023) as f64 as u64, u64::MAX); // rounds up and then becomes unrepresentable
|
||||
assert_eq::<u64>(18446744073709551616.0f64 as u64, u64::MAX);
|
||||
assert_eq::<u64>(f64::MAX as u64, u64::MAX);
|
||||
assert_eq::<u64>(f64::MIN as u64, 0);
|
||||
|
||||
Reference in New Issue
Block a user