implement float_to_int_unchecked

This commit is contained in:
Ralf Jung
2020-04-12 11:35:12 +02:00
parent 8d1f5336c2
commit 78ce616490
2 changed files with 73 additions and 5 deletions
+70 -2
View File
@@ -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),
})
}
}
+3 -3
View File
@@ -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);