1
Fork 0

Use IEEE 754 semantics for NaN (Issue #1084)

This commit is contained in:
Matt Brubeck 2011-10-27 22:01:30 -07:00 committed by Brian Anderson
parent 7e064deacf
commit 000b2fe9a6
4 changed files with 149 additions and 59 deletions

View file

@ -1492,26 +1492,26 @@ tag scalar_type { nil_type; signed_int; unsigned_int; floating_point; }
fn compare_scalar_types(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
t: ty::t, llop: ValueRef) -> result {
let f = bind compare_scalar_values(cx, lhs, rhs, _, llop);
t: ty::t, op: ast::binop) -> result {
let f = bind compare_scalar_values(cx, lhs, rhs, _, op);
alt ty::struct(bcx_tcx(cx), t) {
ty::ty_nil. { ret f(nil_type); }
ty::ty_nil. { ret rslt(cx, f(nil_type)); }
ty::ty_bool. | ty::ty_uint. | ty::ty_ptr(_) | ty::ty_char. {
ret f(unsigned_int);
ret rslt(cx, f(unsigned_int));
}
ty::ty_int. { ret f(signed_int); }
ty::ty_float. { ret f(floating_point); }
ty::ty_int. { ret rslt(cx, f(signed_int)); }
ty::ty_float. { ret rslt(cx, f(floating_point)); }
ty::ty_machine(_) {
if ty::type_is_fp(bcx_tcx(cx), t) {
// Floating point machine types
ret f(floating_point);
ret rslt(cx, f(floating_point));
} else if ty::type_is_signed(bcx_tcx(cx), t) {
// Signed, integral machine types
ret f(signed_int);
ret rslt(cx, f(signed_int));
} else {
// Unsigned, integral machine types
ret f(unsigned_int);
ret rslt(cx, f(unsigned_int));
}
}
ty::ty_type. {
@ -1535,34 +1535,47 @@ fn compare_scalar_types(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
// A helper function to do the actual comparison of scalar values.
fn compare_scalar_values(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
nt: scalar_type, llop: ValueRef) -> result {
let eq_cmp;
let lt_cmp;
let le_cmp;
alt nt {
nt: scalar_type, op: ast::binop) -> ValueRef {
let cmp = alt nt {
nil_type. {
// We don't need to do actual comparisons for nil.
// () == () holds but () < () does not.
eq_cmp = 1u;
lt_cmp = 0u;
le_cmp = 1u;
alt op {
ast::eq. | ast::le. | ast::ge. { 1u }
ast::ne. | ast::lt. | ast::gt. { 0u }
}
}
floating_point. {
eq_cmp = lib::llvm::LLVMRealUEQ;
lt_cmp = lib::llvm::LLVMRealULT;
le_cmp = lib::llvm::LLVMRealULE;
alt op {
ast::eq. { lib::llvm::LLVMRealOEQ }
ast::ne. { lib::llvm::LLVMRealUNE }
ast::lt. { lib::llvm::LLVMRealOLT }
ast::le. { lib::llvm::LLVMRealOLE }
ast::gt. { lib::llvm::LLVMRealOGT }
ast::ge. { lib::llvm::LLVMRealOGE }
}
}
signed_int. {
eq_cmp = lib::llvm::LLVMIntEQ;
lt_cmp = lib::llvm::LLVMIntSLT;
le_cmp = lib::llvm::LLVMIntSLE;
alt op {
ast::eq. { lib::llvm::LLVMIntEQ }
ast::ne. { lib::llvm::LLVMIntNE }
ast::lt. { lib::llvm::LLVMIntSLT }
ast::le. { lib::llvm::LLVMIntSLE }
ast::gt. { lib::llvm::LLVMIntSGT }
ast::ge. { lib::llvm::LLVMIntSGE }
}
}
unsigned_int. {
eq_cmp = lib::llvm::LLVMIntEQ;
lt_cmp = lib::llvm::LLVMIntULT;
le_cmp = lib::llvm::LLVMIntULE;
alt op {
ast::eq. { lib::llvm::LLVMIntEQ }
ast::ne. { lib::llvm::LLVMIntNE }
ast::lt. { lib::llvm::LLVMIntULT }
ast::le. { lib::llvm::LLVMIntULE }
ast::gt. { lib::llvm::LLVMIntUGT }
ast::ge. { lib::llvm::LLVMIntUGE }
}
}
}
};
// FIXME: This wouldn't be necessary if we could bind methods off of
// objects and therefore abstract over FCmp and ICmp (issue #435). Then
// we could just write, e.g., "cmp_fn = bind FCmp(cx, _, _, _);" in
@ -1579,26 +1592,7 @@ fn compare_scalar_values(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
} else { r = ICmp(cx, op, lhs, rhs); }
ret r;
}
let last_cx = new_sub_block_ctxt(cx, "last");
let eq_cx = new_sub_block_ctxt(cx, "eq");
let eq_result = generic_cmp(eq_cx, nt, eq_cmp, lhs, rhs);
Br(eq_cx, last_cx.llbb);
let lt_cx = new_sub_block_ctxt(cx, "lt");
let lt_result = generic_cmp(lt_cx, nt, lt_cmp, lhs, rhs);
Br(lt_cx, last_cx.llbb);
let le_cx = new_sub_block_ctxt(cx, "le");
let le_result = generic_cmp(le_cx, nt, le_cmp, lhs, rhs);
Br(le_cx, last_cx.llbb);
let unreach_cx = new_sub_block_ctxt(cx, "unreach");
Unreachable(unreach_cx);
let llswitch = Switch(cx, llop, unreach_cx.llbb, 3u);
AddCase(llswitch, C_u8(abi::cmp_glue_op_eq), eq_cx.llbb);
AddCase(llswitch, C_u8(abi::cmp_glue_op_lt), lt_cx.llbb);
AddCase(llswitch, C_u8(abi::cmp_glue_op_le), le_cx.llbb);
let last_result =
Phi(last_cx, T_i1(), [eq_result, lt_result, le_result],
[eq_cx.llbb, lt_cx.llbb, le_cx.llbb]);
ret rslt(last_cx, last_result);
ret generic_cmp(cx, nt, cmp, lhs, rhs);
}
type val_pair_fn = fn(@block_ctxt, ValueRef, ValueRef) -> @block_ctxt;
@ -1912,16 +1906,6 @@ fn call_cmp_glue(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef, t: ty::t,
ret rslt(bcx, Load(bcx, llcmpresultptr));
}
// Compares two values. Performs the simple scalar comparison if the types are
// scalar and calls to comparison glue otherwise.
fn compare(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef, t: ty::t,
llop: ValueRef) -> result {
if ty::type_is_scalar(bcx_tcx(cx), t) {
ret compare_scalar_types(cx, lhs, rhs, t, llop);
}
ret call_cmp_glue(cx, lhs, rhs, t, llop);
}
fn take_ty(cx: @block_ctxt, v: ValueRef, t: ty::t) -> @block_ctxt {
if ty::type_has_pointers(bcx_tcx(cx), t) {
ret call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue);
@ -2262,6 +2246,11 @@ fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span,
fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
_lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result {
if ty::type_is_scalar(bcx_tcx(cx), rhs_t) {
let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op);
ret rslt(rs.bcx, rs.val);
}
// Determine the operation we need.
let llop;
alt op {
@ -2270,7 +2259,7 @@ fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
ast::le. | ast::gt. { llop = C_u8(abi::cmp_glue_op_le); }
}
let rs = compare(cx, lhs, rhs, rhs_t, llop);
let rs = call_cmp_glue(cx, lhs, rhs, rhs_t, llop);
// Invert the result if necessary.
alt op {

View file

@ -213,6 +213,9 @@ fn NaN() -> float {
ret 0./0.;
}
/* Predicate: isNaN */
pure fn isNaN(f: float) -> bool { f != f }
/* Function: infinity */
pure fn infinity() -> float {
ret 1./0.;

View file

@ -0,0 +1,74 @@
use std;
import std::float;
fn main() {
let nan = float::NaN();
assert(float::isNaN(nan));
let inf = float::infinity();
assert(-inf == float::neg_infinity());
assert( nan != nan);
assert( nan != -nan);
assert(-nan != -nan);
assert(-nan != nan);
assert( nan != 1.);
assert( nan != 0.);
assert( nan != inf);
assert( nan != -inf);
assert( 1. != nan);
assert( 0. != nan);
assert( inf != nan);
assert(-inf != nan);
assert(!( nan == nan));
assert(!( nan == -nan));
assert(!( nan == 1.));
assert(!( nan == 0.));
assert(!( nan == inf));
assert(!( nan == -inf));
assert(!( 1. == nan));
assert(!( 0. == nan));
assert(!( inf == nan));
assert(!(-inf == nan));
assert(!(-nan == nan));
assert(!(-nan == -nan));
assert(!( nan > nan));
assert(!( nan > -nan));
assert(!( nan > 0.));
assert(!( nan > inf));
assert(!( nan > -inf));
assert(!( 0. > nan));
assert(!( inf > nan));
assert(!(-inf > nan));
assert(!(-nan > nan));
assert(!(nan < 0.));
assert(!(nan < 1.));
assert(!(nan < -1.));
assert(!(nan < inf));
assert(!(nan < -inf));
assert(!(nan < nan));
assert(!(nan < -nan));
assert(!( 0. < nan));
assert(!( 1. < nan));
assert(!( -1. < nan));
assert(!( inf < nan));
assert(!(-inf < nan));
assert(!(-nan < nan));
assert(float::isNaN(nan + inf));
assert(float::isNaN(nan + -inf));
assert(float::isNaN(nan + 0.));
assert(float::isNaN(nan + 1.));
assert(float::isNaN(nan * 1.));
assert(float::isNaN(nan / 1.));
assert(float::isNaN(nan / 0.));
assert(float::isNaN(0. / 0.));
assert(float::isNaN(-inf + inf));
assert(float::isNaN(inf - inf));
}

View file

@ -10,7 +10,7 @@ fn test_from_str() {
assert ( float::from_str("2.5e10") == 25000000000. );
assert ( float::from_str("25000000000.E-10") == 2.5 );
assert ( float::from_str("") == 0. );
assert ( float::from_str(" ") == 0. );
assert ( float::isNaN(float::from_str(" ")) );
assert ( float::from_str(".") == 0. );
assert ( float::from_str("5.") == 5. );
assert ( float::from_str(".5") == 0.5 );
@ -25,6 +25,7 @@ fn test_positive() {
assert(!float::positive(-1.));
assert(!float::positive(float::neg_infinity()));
assert(!float::positive(1./float::neg_infinity()));
assert(!float::positive(float::NaN()));
}
#[test]
@ -35,4 +36,27 @@ fn test_negative() {
assert(float::negative(-1.));
assert(float::negative(float::neg_infinity()));
assert(float::negative(1./float::neg_infinity()));
assert(!float::negative(float::NaN()));
}
#[test]
fn test_nonpositive() {
assert(!float::nonpositive(float::infinity()));
assert(!float::nonpositive(1.));
assert(!float::nonpositive(0.));
assert(float::nonpositive(-1.));
assert(float::nonpositive(float::neg_infinity()));
assert(float::nonpositive(1./float::neg_infinity()));
// TODO: assert(!float::nonpositive(float::NaN()));
}
#[test]
fn test_nonnegative() {
assert(float::nonnegative(float::infinity()));
assert(float::nonnegative(1.));
assert(float::nonnegative(0.));
assert(!float::nonnegative(-1.));
assert(!float::nonnegative(float::neg_infinity()));
assert(!float::nonnegative(1./float::neg_infinity()));
// TODO: assert(!float::nonnegative(float::NaN()));
}