diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index 0979cd7f06..1f90042219 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -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, Round}; +use rustc_target::abi::{Align, LayoutOf, Size}; use crate::*; @@ -279,6 +280,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx 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" @@ -493,4 +510,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.go_to_block(ret); Ok(()) } + + fn float_to_int_unchecked( + &self, + f: F, + dest_ty: ty::Ty<'tcx>, + ) -> InterpResult<'tcx, Scalar> + where + F: Float + Into> + { + 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), + }) + } } diff --git a/tests/compile-fail/intrinsics/copy_overlapping.rs b/tests/compile-fail/intrinsics/copy_overlapping.rs index 76e1e20d21..8d3c681393 100644 --- a/tests/compile-fail/intrinsics/copy_overlapping.rs +++ b/tests/compile-fail/intrinsics/copy_overlapping.rs @@ -1,4 +1,3 @@ -//error-pattern: copy_nonoverlapping called on overlapping ranges #![feature(intrinsics)] // Directly call intrinsic to avoid debug assertions in libstd @@ -11,6 +10,6 @@ fn main() { unsafe { let a = data.as_mut_ptr(); let b = a.wrapping_offset(1) as *mut _; - copy_nonoverlapping(a, b, 2); + copy_nonoverlapping(a, b, 2); //~ ERROR copy_nonoverlapping called on overlapping ranges } } diff --git a/tests/compile-fail/intrinsics/copy_unaligned.rs b/tests/compile-fail/intrinsics/copy_unaligned.rs index a2a4762239..84f4de9346 100644 --- a/tests/compile-fail/intrinsics/copy_unaligned.rs +++ b/tests/compile-fail/intrinsics/copy_unaligned.rs @@ -1,4 +1,3 @@ -//error-pattern: accessing memory with alignment 1, but alignment 2 is required #![feature(intrinsics)] // Directly call intrinsic to avoid debug assertions in libstd @@ -10,5 +9,5 @@ fn main() { let mut data = [0u16; 8]; let ptr = (&mut data[0] as *mut u16 as *mut u8).wrapping_add(1) as *mut u16; // Even copying 0 elements to something unaligned should error - unsafe { copy_nonoverlapping(&data[5], ptr, 0); } + unsafe { copy_nonoverlapping(&data[5], ptr, 0); } //~ ERROR accessing memory with alignment 1, but alignment 2 is required } diff --git a/tests/compile-fail/intrinsics/float_to_int_32_inf1.rs b/tests/compile-fail/intrinsics/float_to_int_32_inf1.rs new file mode 100644 index 0000000000..a56f4aefad --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_inf1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f32::INFINITY); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_infneg1.rs b/tests/compile-fail/intrinsics/float_to_int_32_infneg1.rs new file mode 100644 index 0000000000..d18f75fcca --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_infneg1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f32::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_nan.rs b/tests/compile-fail/intrinsics/float_to_int_32_nan.rs new file mode 100644 index 0000000000..e1fe8c7cf2 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_nan.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f32::NAN); } //~ ERROR: cannot be represented in target type `u32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_nanneg.rs b/tests/compile-fail/intrinsics/float_to_int_32_nanneg.rs new file mode 100644 index 0000000000..38899045c9 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_nanneg.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-f32::NAN); } //~ ERROR: cannot be represented in target type `u32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_neg.rs b/tests/compile-fail/intrinsics/float_to_int_32_neg.rs new file mode 100644 index 0000000000..f15cf9a9cd --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_neg.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-1.000000001f32); } //~ ERROR: cannot be represented in target type `u32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_too_big1.rs b/tests/compile-fail/intrinsics/float_to_int_32_too_big1.rs new file mode 100644 index 0000000000..ccbf917c8e --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_too_big1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(2147483648.0f32); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_too_big2.rs b/tests/compile-fail/intrinsics/float_to_int_32_too_big2.rs new file mode 100644 index 0000000000..6598fd36e0 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_too_big2.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::((u32::MAX-127) as f32); } //~ ERROR: cannot be represented in target type `u32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_32_too_small1.rs b/tests/compile-fail/intrinsics/float_to_int_32_too_small1.rs new file mode 100644 index 0000000000..89f09e1e3f --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_32_too_small1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-2147483904.0f32); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_inf1.rs b/tests/compile-fail/intrinsics/float_to_int_64_inf1.rs new file mode 100644 index 0000000000..e1a7b818d8 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_inf1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f64::INFINITY); } //~ ERROR: cannot be represented in target type `u128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_infneg1.rs b/tests/compile-fail/intrinsics/float_to_int_64_infneg1.rs new file mode 100644 index 0000000000..a1d757b151 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_infneg1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f64::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `u128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_infneg2.rs b/tests/compile-fail/intrinsics/float_to_int_64_infneg2.rs new file mode 100644 index 0000000000..e48d19f1a6 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_infneg2.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f64::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `i128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_nan.rs b/tests/compile-fail/intrinsics/float_to_int_64_nan.rs new file mode 100644 index 0000000000..03f378f5bc --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_nan.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(f64::NAN); } //~ ERROR: cannot be represented in target type `u32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_neg.rs b/tests/compile-fail/intrinsics/float_to_int_64_neg.rs new file mode 100644 index 0000000000..d0b5a3e21c --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_neg.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-1.0000000000001f64); } //~ ERROR: cannot be represented in target type `u128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_big1.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_big1.rs new file mode 100644 index 0000000000..f928f16187 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_big1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(2147483648.0f64); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_big2.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_big2.rs new file mode 100644 index 0000000000..feb24c362d --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_big2.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(9223372036854775808.0f64); } //~ ERROR: cannot be represented in target type `i64` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_big3.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_big3.rs new file mode 100644 index 0000000000..cd491bfed7 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_big3.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(18446744073709551616.0f64); } //~ ERROR: cannot be represented in target type `u64` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_big4.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_big4.rs new file mode 100644 index 0000000000..e9623dba94 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_big4.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(u128::MAX as f64); } //~ ERROR: cannot be represented in target type `u128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_big5.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_big5.rs new file mode 100644 index 0000000000..9c31c690b4 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_big5.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(240282366920938463463374607431768211455.0f64); } //~ ERROR: cannot be represented in target type `i128` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_small1.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_small1.rs new file mode 100644 index 0000000000..08f2f9e3fd --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_small1.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-2147483649.0f64); } //~ ERROR: cannot be represented in target type `i32` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_small2.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_small2.rs new file mode 100644 index 0000000000..f7b205de53 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_small2.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-9223372036854777856.0f64); } //~ ERROR: cannot be represented in target type `i64` +} diff --git a/tests/compile-fail/intrinsics/float_to_int_64_too_small3.rs b/tests/compile-fail/intrinsics/float_to_int_64_too_small3.rs new file mode 100644 index 0000000000..779441f744 --- /dev/null +++ b/tests/compile-fail/intrinsics/float_to_int_64_too_small3.rs @@ -0,0 +1,10 @@ +#![feature(intrinsics)] + +// Directly call intrinsic to avoid debug assertions in libstd +extern "rust-intrinsic" { + fn float_to_int_unchecked(value: Float) -> Int; +} + +fn main() { + unsafe { float_to_int_unchecked::(-240282366920938463463374607431768211455.0f64); } //~ ERROR: cannot be represented in target type `i128` +} diff --git a/tests/run-pass/float.rs b/tests/run-pass/float.rs index 738df2f6c5..364388571f 100644 --- a/tests/run-pass/float.rs +++ b/tests/run-pass/float.rs @@ -10,6 +10,71 @@ fn assert_eq(x: T, y: T) { assert_eq!(x, y); } +trait FloatToInt: Copy { + fn cast(self) -> Int; + unsafe fn cast_unchecked(self) -> Int; +} + +impl FloatToInt for f32 { + fn cast(self) -> i8 { self as _ } + unsafe fn cast_unchecked(self) -> i8 { self.to_int_unchecked() } +} +impl FloatToInt for f32 { + fn cast(self) -> i32 { self as _ } + unsafe fn cast_unchecked(self) -> i32 { self.to_int_unchecked() } +} +impl FloatToInt for f32 { + fn cast(self) -> u32 { self as _ } + unsafe fn cast_unchecked(self) -> u32 { self.to_int_unchecked() } +} +impl FloatToInt for f32 { + fn cast(self) -> i64 { self as _ } + unsafe fn cast_unchecked(self) -> i64 { self.to_int_unchecked() } +} +impl FloatToInt for f32 { + fn cast(self) -> u64 { self as _ } + unsafe fn cast_unchecked(self) -> u64 { self.to_int_unchecked() } +} + +impl FloatToInt for f64 { + fn cast(self) -> i8 { self as _ } + unsafe fn cast_unchecked(self) -> i8 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> i32 { self as _ } + unsafe fn cast_unchecked(self) -> i32 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> u32 { self as _ } + unsafe fn cast_unchecked(self) -> u32 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> i64 { self as _ } + unsafe fn cast_unchecked(self) -> i64 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> u64 { self as _ } + unsafe fn cast_unchecked(self) -> u64 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> i128 { self as _ } + unsafe fn cast_unchecked(self) -> i128 { self.to_int_unchecked() } +} +impl FloatToInt for f64 { + fn cast(self) -> u128 { self as _ } + unsafe fn cast_unchecked(self) -> u128 { self.to_int_unchecked() } +} + +/// Test this cast both via `as` and via `approx_unchecked` (i.e., it must not saturate). +#[track_caller] +#[inline(never)] +fn test_cast(x: F, y: I) + where F: FloatToInt, I: PartialEq + Debug +{ + assert_eq!(x.cast(), y); + assert_eq!(unsafe { x.cast_unchecked() }, y); +} + fn main() { basic(); casts(); @@ -50,19 +115,23 @@ fn basic() { } fn casts() { + // f32 -> i8 + test_cast::(127.99, 127); + test_cast::(-128.99, -128); + // f32 -> i32 - assert_eq::(0.0f32 as i32, 0); - assert_eq::(-0.0f32 as i32, 0); - assert_eq::(/*0x1p-149*/ f32::from_bits(0x00000001) as i32, 0); - assert_eq::(/*-0x1p-149*/ f32::from_bits(0x80000001) as i32, 0); - assert_eq::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd) as i32, 1); - assert_eq::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd) as i32, -1); - assert_eq::(1.9f32 as i32, 1); - assert_eq::(-1.9f32 as i32, -1); - assert_eq::(5.0f32 as i32, 5); - assert_eq::(-5.0f32 as i32, -5); - assert_eq::(2147483520.0f32 as i32, 2147483520); - assert_eq::(-2147483648.0f32 as i32, -2147483648); + test_cast::(0.0, 0); + test_cast::(-0.0, 0); + test_cast::(/*0x1p-149*/ f32::from_bits(0x00000001), 0); + test_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_cast::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1); + test_cast::(1.9, 1); + test_cast::(-1.9, -1); + test_cast::(5.0, 5); + test_cast::(-5.0, -5); + test_cast::(2147483520.0, 2147483520); + test_cast::(-2147483648.0, -2147483648); // unrepresentable casts assert_eq::(2147483648.0f32 as i32, i32::MAX); assert_eq::(-2147483904.0f32 as i32, i32::MIN); @@ -74,20 +143,21 @@ fn casts() { assert_eq::((-f32::NAN) as i32, 0); // f32 -> u32 - assert_eq::(0.0f32 as u32, 0); - assert_eq::(-0.0f32 as u32, 0); - assert_eq::(/*0x1p-149*/ f32::from_bits(0x1) as u32, 0); - assert_eq::(/*-0x1p-149*/ f32::from_bits(0x80000001) as u32, 0); - assert_eq::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd) as u32, 1); - assert_eq::(1.9f32 as u32, 1); - assert_eq::(5.0f32 as u32, 5); - assert_eq::(2147483648.0f32 as u32, 0x8000_0000); - assert_eq::(4294967040.0f32 as u32, 0u32.wrapping_sub(256)); - assert_eq::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666) as u32, 0); - assert_eq::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff) as u32, 0); - assert_eq::((u32::MAX-127) as f32 as u32, u32::MAX); // rounding loss - assert_eq::((u32::MAX-128) as f32 as u32, u32::MAX-255); // rounding loss + test_cast::(0.0, 0); + test_cast::(-0.0, 0); + test_cast::(-0.9999999, 0); + test_cast::(/*0x1p-149*/ f32::from_bits(0x1), 0); + test_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_cast::(1.9, 1); + test_cast::(5.0, 5); + test_cast::(2147483648.0, 0x8000_0000); + test_cast::(4294967040.0, 0u32.wrapping_sub(256)); + test_cast::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0); + test_cast::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0); + test_cast::((u32::MAX-128) as f32, u32::MAX-255); // rounding loss // unrepresentable casts + assert_eq::((u32::MAX-127) as f32 as u32, u32::MAX); // rounds up and then becomes unrepresentable assert_eq::(4294967296.0f32 as u32, u32::MAX); assert_eq::(-5.0f32 as u32, 0); assert_eq::(f32::MAX as u32, u32::MAX); @@ -98,40 +168,44 @@ fn casts() { assert_eq::((-f32::NAN) as u32, 0); // f32 -> i64 - assert_eq::(4294967296.0f32 as i64, 4294967296); - assert_eq::(-4294967296.0f32 as i64, -4294967296); - assert_eq::(9223371487098961920.0f32 as i64, 9223371487098961920); - assert_eq::(-9223372036854775808.0f32 as i64, -9223372036854775808); + test_cast::(4294967296.0, 4294967296); + test_cast::(-4294967296.0, -4294967296); + test_cast::(9223371487098961920.0, 9223371487098961920); + test_cast::(-9223372036854775808.0, -9223372036854775808); + + // f64 -> i8 + test_cast::(127.99, 127); + test_cast::(-128.99, -128); // f64 -> i32 - assert_eq::(0.0f64 as i32, 0); - assert_eq::(-0.0f64 as i32, 0); - assert_eq::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a) as i32, 1); - assert_eq::(/*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a) as i32, -1); - assert_eq::(1.9f64 as i32, 1); - assert_eq::(-1.9f64 as i32, -1); - assert_eq::(1e8f64 as i32, 100_000_000); - assert_eq::(2147483647.0f64 as i32, 2147483647); - assert_eq::(-2147483648.0f64 as i32, -2147483648); + test_cast::(0.0, 0); + test_cast::(-0.0, 0); + test_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_cast::(/*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), -1); + test_cast::(1.9, 1); + test_cast::(-1.9, -1); + test_cast::(1e8, 100_000_000); + test_cast::(2147483647.0, 2147483647); + test_cast::(-2147483648.0, -2147483648); // unrepresentable casts assert_eq::(2147483648.0f64 as i32, i32::MAX); assert_eq::(-2147483649.0f64 as i32, i32::MIN); // f64 -> i64 - assert_eq::(0.0f64 as i64, 0); - assert_eq::(-0.0f64 as i64, 0); - assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as i64, 0); - assert_eq::(/*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001) as i64, 0); - assert_eq::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a) as i64, 1); - assert_eq::(/*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a) as i64, -1); - assert_eq::(5.0f64 as i64, 5); - assert_eq::(5.9f64 as i64, 5); - assert_eq::(-5.0f64 as i64, -5); - assert_eq::(-5.9f64 as i64, -5); - assert_eq::(4294967296.0f64 as i64, 4294967296); - assert_eq::(-4294967296.0f64 as i64, -4294967296); - assert_eq::(9223372036854774784.0f64 as i64, 9223372036854774784); - assert_eq::(-9223372036854775808.0f64 as i64, -9223372036854775808); + test_cast::(0.0, 0); + test_cast::(-0.0, 0); + test_cast::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0); + test_cast::(/*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001), 0); + test_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_cast::(/*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), -1); + test_cast::(5.0, 5); + test_cast::(5.9, 5); + test_cast::(-5.0, -5); + test_cast::(-5.9, -5); + test_cast::(4294967296.0, 4294967296); + test_cast::(-4294967296.0, -4294967296); + test_cast::(9223372036854774784.0, 9223372036854774784); + test_cast::(-9223372036854775808.0, -9223372036854775808); // unrepresentable casts assert_eq::(9223372036854775808.0f64 as i64, i64::MAX); assert_eq::(-9223372036854777856.0f64 as i64, i64::MIN); @@ -143,15 +217,16 @@ fn casts() { assert_eq::((-f64::NAN) as i64, 0); // f64 -> u64 - assert_eq::(0.0f64 as u64, 0); - assert_eq::(-0.0f64 as u64, 0); - assert_eq::(5.0f64 as u64, 5); - assert_eq::(-5.0f64 as u64, 0); - assert_eq::(1e16f64 as u64, 10000000000000000); - assert_eq::((u64::MAX-1023) as f64 as u64, u64::MAX); // rounding loss - assert_eq::((u64::MAX-1024) as f64 as u64, u64::MAX-2047); // rounding loss - assert_eq::(9223372036854775808.0f64 as u64, 9223372036854775808); + test_cast::(0.0, 0); + test_cast::(-0.0, 0); + test_cast::(-0.99999999999, 0); + test_cast::(5.0, 5); + test_cast::(1e16, 10000000000000000); + test_cast::((u64::MAX-1024) as f64, u64::MAX-2047); // rounding loss + test_cast::(9223372036854775808.0, 9223372036854775808); // unrepresentable casts + assert_eq::(-5.0f64 as u64, 0); + assert_eq::((u64::MAX-1023) as f64 as u64, u64::MAX); // rounds up and then becomes unrepresentable assert_eq::(18446744073709551616.0f64 as u64, u64::MAX); assert_eq::(f64::MAX as u64, u64::MAX); assert_eq::(f64::MIN as u64, 0); @@ -160,6 +235,14 @@ fn casts() { assert_eq::(f64::NAN as u64, 0); assert_eq::((-f64::NAN) as u64, 0); + // f64 -> i128 + assert_eq::(f64::MAX as i128, i128::MAX); + assert_eq::(f64::MIN as i128, i128::MIN); + + // f64 -> u128 + assert_eq::(f64::MAX as u128, u128::MAX); + assert_eq::(f64::MIN as u128, 0); + // int -> f32 assert_eq::(127i8 as f32, 127.0); assert_eq::(2147483647i32 as f32, 2147483648.0); @@ -210,10 +293,8 @@ fn casts() { assert_eq::(5.0f64 as f32, 5.0f32); assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0); assert_eq::(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0); - assert_eq::(/*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32, /*0x1p-149*/ f32::from_bits(0x800000)); assert_eq::(/*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32, /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728)); - assert_eq::(f64::MAX as f32, f32::INFINITY); assert_eq::(f64::MIN as f32, f32::NEG_INFINITY); assert_eq::(f64::INFINITY as f32, f32::INFINITY);