Extract (f32::MAX + 0.5 ULP) constant

This commit is contained in:
Robin Kruppe 2017-10-15 21:37:09 +02:00
parent 964ba2a6e7
commit e999e7b8b2
3 changed files with 15 additions and 6 deletions

View File

@ -203,3 +203,11 @@ impl ::std::ops::Neg for ConstFloat {
ConstFloat { bits, ty: self.ty } ConstFloat { bits, ty: self.ty }
} }
} }
/// This is `f32::MAX + (0.5 ULP)` as an integer. Numbers greater or equal to this
/// are rounded to infinity when converted to `f32`.
///
/// NB: Computed as maximum significand with an extra 1 bit added (for the half ULP)
/// shifted by the maximum exponent (accounting for normalization).
pub const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1)
<< (Single::MAX_EXP - Single::PRECISION as i16);

View File

@ -11,7 +11,7 @@
use llvm::{self, ValueRef}; use llvm::{self, ValueRef};
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind}; use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
use rustc_const_math::ConstInt::*; use rustc_const_math::ConstInt::*;
use rustc_const_math::{ConstInt, ConstMathErr}; use rustc_const_math::{ConstInt, ConstMathErr, MAX_F32_PLUS_HALF_ULP};
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::infer::TransNormalize; use rustc::infer::TransNormalize;
use rustc::traits; use rustc::traits;
@ -986,10 +986,10 @@ unsafe fn const_cast_int_to_float(ccx: &CrateContext,
panic!("could not get z128 value of constant integer {:?}", panic!("could not get z128 value of constant integer {:?}",
Value(llval)); Value(llval));
}); });
// If this is an u128 cast and the value is > f32::MAX + 0.5 ULP, round up to infinity.
if signed { if signed {
llvm::LLVMConstSIToFP(llval, float_ty.to_ref()) llvm::LLVMConstSIToFP(llval, float_ty.to_ref())
} else if value >= 0xffffff80000000000000000000000000_u128 && float_ty.float_width() == 32 { } else if float_ty.float_width() == 32 && value >= MAX_F32_PLUS_HALF_ULP {
// We're casting to f32 and the value is > f32::MAX + 0.5 ULP -> round up to infinity.
let infinity_bits = C_u32(ccx, ieee::Single::INFINITY.to_bits() as u32); let infinity_bits = C_u32(ccx, ieee::Single::INFINITY.to_bits() as u32);
consts::bitcast(infinity_bits, float_ty) consts::bitcast(infinity_bits, float_ty)
} else { } else {

View File

@ -16,6 +16,7 @@ use rustc::mir::tcx::LvalueTy;
use rustc::mir; use rustc::mir;
use rustc::middle::lang_items::ExchangeMallocFnLangItem; use rustc::middle::lang_items::ExchangeMallocFnLangItem;
use rustc_apfloat::{ieee, Float, Status, Round}; use rustc_apfloat::{ieee, Float, Status, Round};
use rustc_const_math::MAX_F32_PLUS_HALF_ULP;
use std::{u128, i128}; use std::{u128, i128};
use base; use base;
@ -827,9 +828,9 @@ fn cast_int_to_float(bcx: &Builder,
// LLVM's uitofp produces undef in those cases, so we manually check for that case. // 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; 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 && bcx.sess().opts.debugging_opts.saturating_float_casts {
// f32::MAX + 0.5 ULP as u128. All inputs greater or equal to this should be // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity,
// rounded to infinity, for everything else LLVM's uitofp works just fine. // and for everything else LLVM's uitofp works just fine.
let max = C_big_integral(int_ty, 0xffffff80000000000000000000000000_u128); let max = C_big_integral(int_ty, MAX_F32_PLUS_HALF_ULP);
let overflow = bcx.icmp(llvm::IntUGE, x, max); let overflow = bcx.icmp(llvm::IntUGE, x, max);
let infinity_bits = C_u32(bcx.ccx, ieee::Single::INFINITY.to_bits() as u32); let infinity_bits = C_u32(bcx.ccx, ieee::Single::INFINITY.to_bits() as u32);
let infinity = consts::bitcast(infinity_bits, float_ty); let infinity = consts::bitcast(infinity_bits, float_ty);