-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Saturating casts between integers and floats #45205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+535
−16
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0d6b52c
Saturating casts between integers and floats (both directions).
964ba2a
Fix bug in rustc_apfloat
e999e7b
Extract (f32::MAX + 0.5 ULP) constant
354a5cb
Make trans const eval error on overflow and NaN, matching HIR const …
0a843df
Implement more efficient saturation
ce46649
Clean up
ef0b999
Disable u128 <-> float tests on emscripten
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// compile-flags: -C no-prepopulate-passes | ||
|
||
// This file tests that we don't generate any code for saturation if | ||
// -Z saturating-float-casts is not enabled. | ||
|
||
#![crate_type = "lib"] | ||
#![feature(i128_type)] | ||
|
||
// CHECK-LABEL: @f32_to_u32 | ||
#[no_mangle] | ||
pub fn f32_to_u32(x: f32) -> u32 { | ||
// CHECK: fptoui | ||
// CHECK-NOT: fcmp | ||
// CHECK-NOT: icmp | ||
// CHECK-NOT: select | ||
x as u32 | ||
} | ||
|
||
// CHECK-LABEL: @f32_to_i32 | ||
#[no_mangle] | ||
pub fn f32_to_i32(x: f32) -> i32 { | ||
// CHECK: fptosi | ||
// CHECK-NOT: fcmp | ||
// CHECK-NOT: icmp | ||
// CHECK-NOT: select | ||
x as i32 | ||
} | ||
|
||
#[no_mangle] | ||
pub fn f64_to_u8(x: f32) -> u16 { | ||
// CHECK-NOT: fcmp | ||
// CHECK-NOT: icmp | ||
// CHECK-NOT: select | ||
x as u16 | ||
} | ||
|
||
// CHECK-LABEL: @i32_to_f64 | ||
#[no_mangle] | ||
pub fn i32_to_f64(x: i32) -> f64 { | ||
// CHECK: sitofp | ||
// CHECK-NOT: fcmp | ||
// CHECK-NOT: icmp | ||
// CHECK-NOT: select | ||
x as f64 | ||
} | ||
|
||
// CHECK-LABEL: @u128_to_f32 | ||
#[no_mangle] | ||
pub fn u128_to_f32(x: u128) -> f32 { | ||
// CHECK: uitofp | ||
// CHECK-NOT: fcmp | ||
// CHECK-NOT: icmp | ||
// CHECK-NOT: select | ||
x as f32 | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#![feature(i128_type)] | ||
#![allow(const_err)] // this test is only about hard errors | ||
|
||
use std::{f32, f64}; | ||
|
||
// Forces evaluation of constants, triggering hard error | ||
fn force<T>(_: T) {} | ||
|
||
fn main() { | ||
{ const X: u16 = -1. as u16; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = -100. as u128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: i8 = f32::NAN as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i32 = f32::NAN as i32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u64 = f32::NAN as u64; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = f32::NAN as u128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: i8 = f32::INFINITY as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u32 = f32::INFINITY as u32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i128 = f32::INFINITY as i128; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = f32::INFINITY as u128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: u8 = f32::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u16 = f32::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i64 = f32::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i128 = f32::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: i8 = f64::NAN as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i32 = f64::NAN as i32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u64 = f64::NAN as u64; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = f64::NAN as u128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: i8 = f64::INFINITY as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u32 = f64::INFINITY as u32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i128 = f64::INFINITY as i128; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = f64::INFINITY as u128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: u8 = f64::NEG_INFINITY as u8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u16 = f64::NEG_INFINITY as u16; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i64 = f64::NEG_INFINITY as i64; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i128 = f64::NEG_INFINITY as i128; force(X); } //~ ERROR constant evaluation error | ||
|
||
{ const X: u8 = 256. as u8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i8 = -129. as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i8 = 128. as i8; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i32 = 2147483648. as i32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i32 = -2147483904. as i32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u32 = 4294967296. as u32; force(X); } //~ ERROR constant evaluation error | ||
{ const X: u128 = 1e40 as u128; force(X); } //~ ERROR constant evaluation error | ||
{ const X: i128 = 1e40 as i128; force(X); } //~ ERROR constant evaluation error | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// compile-flags: -Z saturating-float-casts | ||
|
||
#![feature(test, i128, i128_type, stmt_expr_attributes)] | ||
#![deny(overflowing_literals)] | ||
extern crate test; | ||
|
||
use std::{f32, f64}; | ||
use std::{u8, i8, u16, i16, u32, i32, u64, i64}; | ||
#[cfg(not(target_os="emscripten"))] | ||
use std::{u128, i128}; | ||
use test::black_box; | ||
|
||
macro_rules! test { | ||
($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ( | ||
// black_box disables constant evaluation to test run-time conversions: | ||
assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, | ||
"run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); | ||
); | ||
|
||
($fval:expr, f* -> $ity:ident, $ival:expr) => ( | ||
test!($fval, f32 -> $ity, $ival); | ||
test!($fval, f64 -> $ity, $ival); | ||
) | ||
} | ||
|
||
// This macro tests const eval in addition to run-time evaluation. | ||
// If and when saturating casts are adopted, this macro should be merged with test!() to ensure | ||
// that run-time and const eval agree on inputs that currently trigger a const eval error. | ||
macro_rules! test_c { | ||
($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ | ||
test!($val, $src_ty -> $dest_ty, $expected); | ||
{ | ||
const X: $src_ty = $val; | ||
const Y: $dest_ty = X as $dest_ty; | ||
assert_eq!(Y, $expected, | ||
"const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); | ||
} | ||
}); | ||
|
||
($fval:expr, f* -> $ity:ident, $ival:expr) => ( | ||
test!($fval, f32 -> $ity, $ival); | ||
test!($fval, f64 -> $ity, $ival); | ||
) | ||
} | ||
|
||
macro_rules! common_fptoi_tests { | ||
($fty:ident -> $($ity:ident)+) => ({ $( | ||
test!($fty::NAN, $fty -> $ity, 0); | ||
test!($fty::INFINITY, $fty -> $ity, $ity::MAX); | ||
test!($fty::NEG_INFINITY, $fty -> $ity, $ity::MIN); | ||
// These two tests are not solely float->int tests, in particular the latter relies on | ||
// `u128::MAX as f32` not being UB. But that's okay, since this file tests int->float | ||
// as well, the test is just slightly misplaced. | ||
test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); | ||
test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); | ||
test_c!(0., $fty -> $ity, 0); | ||
test_c!($fty::MIN_POSITIVE, $fty -> $ity, 0); | ||
test!(-0.9, $fty -> $ity, 0); | ||
test_c!(1., $fty -> $ity, 1); | ||
test_c!(42., $fty -> $ity, 42); | ||
)+ }); | ||
|
||
(f* -> $($ity:ident)+) => ({ | ||
common_fptoi_tests!(f32 -> $($ity)+); | ||
common_fptoi_tests!(f64 -> $($ity)+); | ||
}) | ||
} | ||
|
||
macro_rules! fptoui_tests { | ||
($fty: ident -> $($ity: ident)+) => ({ $( | ||
test!(-0., $fty -> $ity, 0); | ||
test!(-$fty::MIN_POSITIVE, $fty -> $ity, 0); | ||
test!(-0.99999994, $fty -> $ity, 0); | ||
test!(-1., $fty -> $ity, 0); | ||
test!(-100., $fty -> $ity, 0); | ||
test!(#[allow(overflowing_literals)] -1e50, $fty -> $ity, 0); | ||
test!(#[allow(overflowing_literals)] -1e130, $fty -> $ity, 0); | ||
)+ }); | ||
|
||
(f* -> $($ity:ident)+) => ({ | ||
fptoui_tests!(f32 -> $($ity)+); | ||
fptoui_tests!(f64 -> $($ity)+); | ||
}) | ||
} | ||
|
||
pub fn main() { | ||
common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); | ||
fptoui_tests!(f* -> u8 u16 u32 u64); | ||
// FIXME emscripten does not support i128 | ||
#[cfg(not(target_os="emscripten"))] { | ||
common_fptoi_tests!(f* -> i128 u128); | ||
fptoui_tests!(f* -> u128); | ||
} | ||
|
||
// The following tests cover edge cases for some integer types. | ||
|
||
// # u8 | ||
test_c!(254., f* -> u8, 254); | ||
test!(256., f* -> u8, 255); | ||
|
||
// # i8 | ||
test_c!(-127., f* -> i8, -127); | ||
test!(-129., f* -> i8, -128); | ||
test_c!(126., f* -> i8, 126); | ||
test!(128., f* -> i8, 127); | ||
|
||
// # i32 | ||
// -2147483648. is i32::MIN (exactly) | ||
test_c!(-2147483648., f* -> i32, i32::MIN); | ||
// 2147483648. is i32::MAX rounded up | ||
test!(2147483648., f32 -> i32, 2147483647); | ||
// With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to | ||
// multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: | ||
test_c!(2147483520., f32 -> i32, 2147483520); | ||
// Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 | ||
test!(-2147483904., f* -> i32, i32::MIN); | ||
test_c!(-2147483520., f* -> i32, -2147483520); | ||
|
||
// # u32 | ||
// round(MAX) and nextUp(round(MAX)) | ||
test_c!(4294967040., f* -> u32, 4294967040); | ||
test!(4294967296., f* -> u32, 4294967295); | ||
|
||
// # u128 | ||
#[cfg(not(target_os="emscripten"))] | ||
{ | ||
// float->int: | ||
test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); | ||
// nextDown(f32::MAX) = 2^128 - 2 * 2^104 | ||
const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; | ||
test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); | ||
|
||
// int->float: | ||
// f32::MAX - 0.5 ULP and smaller should be rounded down | ||
test_c!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); | ||
test_c!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32); | ||
test_c!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32); | ||
// numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX | ||
test_c!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX); | ||
test_c!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX); | ||
test_c!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX); | ||
test_c!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX); | ||
test_c!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX); | ||
// f32::MAX + 0.5 ULP and greater should be rounded to infinity | ||
test_c!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY); | ||
test_c!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY); | ||
test_c!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY); | ||
|
||
// u128->f64 should not be affected by the u128->f32 checks | ||
test_c!(0xffffff80000000000000000000000000, u128 -> f64, | ||
340282356779733661637539395458142568448.0); | ||
test_c!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arg types does not match function name. Should be either
f32_to_u16(x: f32) -> u16
orf64_to_u8(x: f64) -> u8
.