214 lines
7.9 KiB
Rust
214 lines
7.9 KiB
Rust
// Copyright 2015 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.
|
|
|
|
use std::cmp::Ordering;
|
|
use std::num::ParseFloatError;
|
|
|
|
use syntax::ast;
|
|
|
|
use rustc_apfloat::{Float, FloatConvert, Status};
|
|
use rustc_apfloat::ieee::{Single, Double};
|
|
|
|
use super::err::*;
|
|
|
|
// Note that equality for `ConstFloat` means that the it is the same
|
|
// constant, not that the rust values are equal. In particular, `NaN
|
|
// == NaN` (at least if it's the same NaN; distinct encodings for NaN
|
|
// are considering unequal).
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
|
pub struct ConstFloat {
|
|
pub ty: ast::FloatTy,
|
|
|
|
// This is a bit inefficient but it makes conversions below more
|
|
// ergonomic, and all of this will go away once `miri` is merged.
|
|
pub bits: u128,
|
|
}
|
|
|
|
impl ConstFloat {
|
|
/// Description of the type, not the value
|
|
pub fn description(&self) -> &'static str {
|
|
self.ty.ty_to_string()
|
|
}
|
|
|
|
/// Compares the values if they are of the same type
|
|
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
|
|
match (self.ty, rhs.ty) {
|
|
(ast::FloatTy::F64, ast::FloatTy::F64) => {
|
|
let a = Double::from_bits(self.bits);
|
|
let b = Double::from_bits(rhs.bits);
|
|
// This is pretty bad but it is the existing behavior.
|
|
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
|
|
}
|
|
|
|
(ast::FloatTy::F32, ast::FloatTy::F32) => {
|
|
let a = Single::from_bits(self.bits);
|
|
let b = Single::from_bits(rhs.bits);
|
|
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
|
|
}
|
|
|
|
_ => Err(CmpBetweenUnequalTypes),
|
|
}
|
|
}
|
|
|
|
pub fn from_i128(input: i128, ty: ast::FloatTy) -> Self {
|
|
let bits = match ty {
|
|
ast::FloatTy::F32 => Single::from_i128(input).value.to_bits(),
|
|
ast::FloatTy::F64 => Double::from_i128(input).value.to_bits()
|
|
};
|
|
ConstFloat { bits, ty }
|
|
}
|
|
|
|
pub fn from_u128(input: u128, ty: ast::FloatTy) -> Self {
|
|
let bits = match ty {
|
|
ast::FloatTy::F32 => Single::from_u128(input).value.to_bits(),
|
|
ast::FloatTy::F64 => Double::from_u128(input).value.to_bits()
|
|
};
|
|
ConstFloat { bits, ty }
|
|
}
|
|
|
|
pub fn from_str(num: &str, ty: ast::FloatTy) -> Result<Self, ParseFloatError> {
|
|
let bits = match ty {
|
|
ast::FloatTy::F32 => {
|
|
let rust_bits = num.parse::<f32>()?.to_bits() as u128;
|
|
let apfloat = num.parse::<Single>().unwrap_or_else(|e| {
|
|
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e);
|
|
});
|
|
let apfloat_bits = apfloat.to_bits();
|
|
assert!(rust_bits == apfloat_bits,
|
|
"apfloat::ieee::Single gave different result for `{}`: \
|
|
{}({:#x}) vs Rust's {}({:#x})",
|
|
num, apfloat, apfloat_bits,
|
|
Single::from_bits(rust_bits), rust_bits);
|
|
apfloat_bits
|
|
}
|
|
ast::FloatTy::F64 => {
|
|
let rust_bits = num.parse::<f64>()?.to_bits() as u128;
|
|
let apfloat = num.parse::<Double>().unwrap_or_else(|e| {
|
|
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e);
|
|
});
|
|
let apfloat_bits = apfloat.to_bits();
|
|
assert!(rust_bits == apfloat_bits,
|
|
"apfloat::ieee::Double gave different result for `{}`: \
|
|
{}({:#x}) vs Rust's {}({:#x})",
|
|
num, apfloat, apfloat_bits,
|
|
Double::from_bits(rust_bits), rust_bits);
|
|
apfloat_bits
|
|
}
|
|
};
|
|
Ok(ConstFloat { bits, ty })
|
|
}
|
|
|
|
pub fn to_i128(self, width: usize) -> Option<i128> {
|
|
assert!(width <= 128);
|
|
let r = match self.ty {
|
|
ast::FloatTy::F32 => Single::from_bits(self.bits).to_i128(width),
|
|
ast::FloatTy::F64 => Double::from_bits(self.bits).to_i128(width)
|
|
};
|
|
if r.status.intersects(Status::INVALID_OP) {
|
|
None
|
|
} else {
|
|
Some(r.value)
|
|
}
|
|
}
|
|
|
|
pub fn to_u128(self, width: usize) -> Option<u128> {
|
|
assert!(width <= 128);
|
|
let r = match self.ty {
|
|
ast::FloatTy::F32 => Single::from_bits(self.bits).to_u128(width),
|
|
ast::FloatTy::F64 => Double::from_bits(self.bits).to_u128(width)
|
|
};
|
|
if r.status.intersects(Status::INVALID_OP) {
|
|
None
|
|
} else {
|
|
Some(r.value)
|
|
}
|
|
}
|
|
|
|
pub fn convert(self, to: ast::FloatTy) -> Self {
|
|
let bits = match (self.ty, to) {
|
|
(ast::FloatTy::F32, ast::FloatTy::F32) |
|
|
(ast::FloatTy::F64, ast::FloatTy::F64) => return self,
|
|
|
|
(ast::FloatTy::F32, ast::FloatTy::F64) => {
|
|
Double::to_bits(Single::from_bits(self.bits).convert(&mut false).value)
|
|
}
|
|
(ast::FloatTy::F64, ast::FloatTy::F32) => {
|
|
Single::to_bits(Double::from_bits(self.bits).convert(&mut false).value)
|
|
}
|
|
};
|
|
ConstFloat { bits, ty: to }
|
|
}
|
|
}
|
|
|
|
impl ::std::fmt::Display for ConstFloat {
|
|
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
|
match self.ty {
|
|
ast::FloatTy::F32 => write!(fmt, "{:#}", Single::from_bits(self.bits))?,
|
|
ast::FloatTy::F64 => write!(fmt, "{:#}", Double::from_bits(self.bits))?,
|
|
}
|
|
write!(fmt, "{}", self.ty)
|
|
}
|
|
}
|
|
|
|
impl ::std::fmt::Debug for ConstFloat {
|
|
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
|
::std::fmt::Display::fmt(self, fmt)
|
|
}
|
|
}
|
|
|
|
macro_rules! derive_binop {
|
|
($op:ident, $func:ident) => {
|
|
impl ::std::ops::$op for ConstFloat {
|
|
type Output = Result<Self, ConstMathErr>;
|
|
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
|
|
let bits = match (self.ty, rhs.ty) {
|
|
(ast::FloatTy::F32, ast::FloatTy::F32) =>{
|
|
let a = Single::from_bits(self.bits);
|
|
let b = Single::from_bits(rhs.bits);
|
|
a.$func(b).value.to_bits()
|
|
}
|
|
(ast::FloatTy::F64, ast::FloatTy::F64) => {
|
|
let a = Double::from_bits(self.bits);
|
|
let b = Double::from_bits(rhs.bits);
|
|
a.$func(b).value.to_bits()
|
|
}
|
|
_ => return Err(UnequalTypes(Op::$op)),
|
|
};
|
|
Ok(ConstFloat { bits, ty: self.ty })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
derive_binop!(Add, add);
|
|
derive_binop!(Sub, sub);
|
|
derive_binop!(Mul, mul);
|
|
derive_binop!(Div, div);
|
|
derive_binop!(Rem, rem);
|
|
|
|
impl ::std::ops::Neg for ConstFloat {
|
|
type Output = Self;
|
|
fn neg(self) -> Self {
|
|
let bits = match self.ty {
|
|
ast::FloatTy::F32 => (-Single::from_bits(self.bits)).to_bits(),
|
|
ast::FloatTy::F64 => (-Double::from_bits(self.bits)).to_bits(),
|
|
};
|
|
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);
|