diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index a855fe2bacf7a..4c2abd17f36f4 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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,70 +1535,50 @@ 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; + nt: scalar_type, op: ast::binop) -> ValueRef { 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. { ret C_bool(true); } + ast::ne. | ast::lt. | ast::gt. { ret C_bool(false); } + } } floating_point. { - eq_cmp = lib::llvm::LLVMRealUEQ; - lt_cmp = lib::llvm::LLVMRealULT; - le_cmp = lib::llvm::LLVMRealULE; + let cmp = 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 } + }; + ret FCmp(cx, cmp, lhs, rhs); } signed_int. { - eq_cmp = lib::llvm::LLVMIntEQ; - lt_cmp = lib::llvm::LLVMIntSLT; - le_cmp = lib::llvm::LLVMIntSLE; + let cmp = 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 } + }; + ret ICmp(cx, cmp, lhs, rhs); } unsigned_int. { - eq_cmp = lib::llvm::LLVMIntEQ; - lt_cmp = lib::llvm::LLVMIntULT; - le_cmp = lib::llvm::LLVMIntULE; - } - } - // 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 - // the above, and "auto eq_result = cmp_fn(eq_cmp, lhs, rhs);" in the - // below. - - fn generic_cmp(cx: @block_ctxt, nt: scalar_type, op: uint, lhs: ValueRef, - rhs: ValueRef) -> ValueRef { - let r: ValueRef; - if nt == nil_type { - r = C_bool(op != 0u); - } else if nt == floating_point { - r = FCmp(cx, op, lhs, rhs); - } 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); + let cmp = 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 } + }; + ret ICmp(cx, cmp, lhs, rhs); + } + } } type val_pair_fn = fn(@block_ctxt, ValueRef, ValueRef) -> @block_ctxt; @@ -1912,16 +1892,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 +2232,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 +2245,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 { diff --git a/src/lib/float.rs b/src/lib/float.rs index 5bdf250069e94..c7053486ccb5a 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -213,13 +213,16 @@ fn NaN() -> float { ret 0./0.; } +/* Predicate: isNaN */ +pure fn isNaN(f: float) -> bool { f != f } + /* Function: infinity */ -fn infinity() -> float { +pure fn infinity() -> float { ret 1./0.; } /* Function: neg_infinity */ -fn neg_infinity() -> float { +pure fn neg_infinity() -> float { ret -1./0.; } @@ -256,17 +259,39 @@ pure fn ge(x: float, y: float) -> bool { ret x >= y; } /* Predicate: gt */ pure fn gt(x: float, y: float) -> bool { ret x > y; } -/* Predicate: positive */ -pure fn positive(x: float) -> bool { ret x > 0.; } +/* +Predicate: positive + +Returns true if `x` is a positive number, including +0.0 and +Infinity. + */ +pure fn positive(x: float) -> bool { ret x > 0. || (1./x) == infinity(); } + +/* +Predicate: negative -/* Predicate: negative */ -pure fn negative(x: float) -> bool { ret x < 0.; } +Returns true if `x` is a negative number, including -0.0 and -Infinity. + */ +pure fn negative(x: float) -> bool { ret x < 0. || (1./x) == neg_infinity(); } -/* Predicate: nonpositive */ -pure fn nonpositive(x: float) -> bool { ret x <= 0.; } +/* +Predicate: nonpositive -/* Predicate: nonnegative */ -pure fn nonnegative(x: float) -> bool { ret x >= 0.; } +Returns true if `x` is a negative number, including -0.0 and -Infinity. +(This is the same as `float::negative`.) +*/ +pure fn nonpositive(x: float) -> bool { + ret x < 0. || (1./x) == neg_infinity(); +} + +/* +Predicate: nonnegative + +Returns true if `x` is a positive number, including +0.0 and +Infinity. +(This is the same as `float::positive`.) +*/ +pure fn nonnegative(x: float) -> bool { + ret x > 0. || (1./x) == infinity(); +} // // Local Variables: diff --git a/src/test/run-pass/float-nan.rs b/src/test/run-pass/float-nan.rs new file mode 100644 index 0000000000000..d0ec3d4f9949c --- /dev/null +++ b/src/test/run-pass/float-nan.rs @@ -0,0 +1,82 @@ +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)); + + assert(!float::isNaN(-1.)); + assert(!float::isNaN(0.)); + assert(!float::isNaN(0.1)); + assert(!float::isNaN(1.)); + assert(!float::isNaN(inf)); + assert(!float::isNaN(-inf)); + assert(!float::isNaN(1./-inf)); +} diff --git a/src/test/stdtest/float.rs b/src/test/stdtest/float.rs index b77d83eb75735..d9de78b60090a 100644 --- a/src/test/stdtest/float.rs +++ b/src/test/stdtest/float.rs @@ -10,10 +10,53 @@ 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 ); assert ( float::from_str("0.5") == 0.5 ); +} + +#[test] +fn test_positive() { + assert(float::positive(float::infinity())); + assert(float::positive(1.)); + assert(float::positive(0.)); + assert(!float::positive(-1.)); + assert(!float::positive(float::neg_infinity())); + assert(!float::positive(1./float::neg_infinity())); + assert(!float::positive(float::NaN())); +} +#[test] +fn test_negative() { + assert(!float::negative(float::infinity())); + assert(!float::negative(1.)); + assert(!float::negative(0.)); + 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())); + 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())); + assert(!float::nonnegative(float::NaN())); }