Skip to content

Make saturating u128 -> f32 casts the default behavior #45900

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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
tls_model: Option<String> = (None, parse_opt_string, [TRACKED],
"choose the TLS model to use (rustc --print tls-models for details)"),
saturating_float_casts: bool = (false, parse_bool, [TRACKED],
"make casts between integers and floats safe: clip out-of-range inputs to the min/max \
integer or to infinity respectively, and turn `NAN` into 0 when casting to integers"),
"make float->int casts UB-free: numbers outside the integer type's range are clipped to \
the max/min integer respectively, and NaN is mapped to 0"),
}

pub fn default_lib_output() -> CrateType {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ fn cast_int_to_float(bcx: &Builder,
// It's only u128 -> f32 that can cause overflows (i.e., should yield infinity).
// LLVM's uitofp produces undef in those cases, so we manually check for that case.
let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32;
if is_u128_to_f32 && bcx.sess().opts.debugging_opts.saturating_float_casts {
if is_u128_to_f32 {
// All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity,
// and for everything else LLVM's uitofp works just fine.
let max = C_big_integral(int_ty, MAX_F32_PLUS_HALF_ULP);
Expand Down
23 changes: 2 additions & 21 deletions src/test/codegen/unchecked-float-casts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,10 @@ pub fn f32_to_i32(x: f32) -> i32 {
}

#[no_mangle]
pub fn f64_to_u8(x: f32) -> u16 {
pub fn f64_to_u16(x: f64) -> u16 {
// CHECK: fptoui
// 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
}
22 changes: 1 addition & 21 deletions src/test/run-pass/saturating-float-casts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction.
// compile-flags: -Z saturating-float-casts

#![feature(test, i128, i128_type, stmt_expr_attributes)]
Expand Down Expand Up @@ -139,26 +140,5 @@ pub fn main() {
// 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);
}
}
58 changes: 58 additions & 0 deletions src/test/run-pass/u128-as-f32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.

// ignore-emscripten u128 not supported

#![feature(test, i128, i128_type)]
#![deny(overflowing_literals)]
extern crate test;

use std::f32;
use std::u128;
use test::black_box;

macro_rules! test {
($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({
{
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));
}
// 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));
});
}

pub fn main() {
// nextDown(f32::MAX) = 2^128 - 2 * 2^104
const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.;

// f32::MAX - 0.5 ULP and smaller should be rounded down
test!(0xfffffe00000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32);
test!(0xfffffe7fffffffffffffffffffffffff, u128 -> f32, SECOND_LARGEST_F32);
test!(0xfffffe80000000000000000000000000, u128 -> f32, SECOND_LARGEST_F32);
// numbers within < 0.5 ULP of f32::MAX it should be rounded to f32::MAX
test!(0xfffffe80000000000000000000000001, u128 -> f32, f32::MAX);
test!(0xfffffeffffffffffffffffffffffffff, u128 -> f32, f32::MAX);
test!(0xffffff00000000000000000000000000, u128 -> f32, f32::MAX);
test!(0xffffff00000000000000000000000001, u128 -> f32, f32::MAX);
test!(0xffffff7fffffffffffffffffffffffff, u128 -> f32, f32::MAX);
// f32::MAX + 0.5 ULP and greater should be rounded to infinity
test!(0xffffff80000000000000000000000000, u128 -> f32, f32::INFINITY);
test!(0xffffff80000000f00000000000000000, u128 -> f32, f32::INFINITY);
test!(0xffffff87ffffffffffffffff00000001, u128 -> f32, f32::INFINITY);

// u128->f64 should not be affected by the u128->f32 checks
test!(0xffffff80000000000000000000000000, u128 -> f64,
340282356779733661637539395458142568448.0);
test!(u128::MAX, u128 -> f64, 340282366920938463463374607431768211455.0);
}