From 17ea058d5a6bca9289844122cd269aa294671aa9 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 27 Oct 2011 16:22:16 -0700 Subject: [PATCH 1/5] +0.0 should be positive and -0.0 should be negative. --- src/lib/float.rs | 12 ++++++------ src/test/stdtest/float.rs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/lib/float.rs b/src/lib/float.rs index 5bdf250069e94..55ffa5025e764 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -214,12 +214,12 @@ fn NaN() -> float { } /* 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.; } @@ -257,16 +257,16 @@ pure fn ge(x: float, y: float) -> bool { ret x >= y; } pure fn gt(x: float, y: float) -> bool { ret x > y; } /* Predicate: positive */ -pure fn positive(x: float) -> bool { ret x > 0.; } +pure fn positive(x: float) -> bool { ret x > 0. || (1./x) == infinity(); } /* Predicate: negative */ -pure fn negative(x: float) -> bool { ret x < 0.; } +pure fn negative(x: float) -> bool { ret x < 0. || (1./x) == neg_infinity(); } /* Predicate: nonpositive */ -pure fn nonpositive(x: float) -> bool { ret x <= 0.; } +pure fn nonpositive(x: float) -> bool { ret !positive(x); } /* Predicate: nonnegative */ -pure fn nonnegative(x: float) -> bool { ret x >= 0.; } +pure fn nonnegative(x: float) -> bool { ret !negative(x); } // // Local Variables: diff --git a/src/test/stdtest/float.rs b/src/test/stdtest/float.rs index b77d83eb75735..086942fb8c175 100644 --- a/src/test/stdtest/float.rs +++ b/src/test/stdtest/float.rs @@ -15,5 +15,24 @@ fn test_from_str() { 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())); +} + +#[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())); } From f44c8e87350bd547afb4fc137fd36a3a547a575c Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 27 Oct 2011 22:01:30 -0700 Subject: [PATCH 2/5] Use IEEE 754 semantics for NaN (Issue #1084) --- src/comp/middle/trans.rs | 105 +++++++++++++++------------------ src/lib/float.rs | 3 + src/test/run-pass/float-nan.rs | 74 +++++++++++++++++++++++ src/test/stdtest/float.rs | 26 +++++++- 4 files changed, 149 insertions(+), 59 deletions(-) create mode 100644 src/test/run-pass/float-nan.rs diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index a855fe2bacf7a..8b2f57baf60d5 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,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 { diff --git a/src/lib/float.rs b/src/lib/float.rs index 55ffa5025e764..1b101eaf859be 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -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.; diff --git a/src/test/run-pass/float-nan.rs b/src/test/run-pass/float-nan.rs new file mode 100644 index 0000000000000..61ef4373a8c2a --- /dev/null +++ b/src/test/run-pass/float-nan.rs @@ -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)); +} diff --git a/src/test/stdtest/float.rs b/src/test/stdtest/float.rs index 086942fb8c175..302d6b1679e01 100644 --- a/src/test/stdtest/float.rs +++ b/src/test/stdtest/float.rs @@ -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())); } From f9a8f7c422c3e9b3815c32b53def30ee4176a2db Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 27 Oct 2011 17:45:04 -0700 Subject: [PATCH 3/5] Change behavior of float::nonpositive/nonnegative Rather than being defined as !positive and !negative, these should act the same as negative and positive (respectively). The only effect of this change should be that all four functions will now return false for NaN. --- src/lib/float.rs | 34 ++++++++++++++++++++++++++++------ src/test/stdtest/float.rs | 4 ++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/lib/float.rs b/src/lib/float.rs index 1b101eaf859be..c7053486ccb5a 100644 --- a/src/lib/float.rs +++ b/src/lib/float.rs @@ -259,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 */ +/* +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 + +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 !positive(x); } +/* +Predicate: nonpositive + +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 */ -pure fn nonnegative(x: float) -> bool { ret !negative(x); } +/* +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/stdtest/float.rs b/src/test/stdtest/float.rs index 302d6b1679e01..d9de78b60090a 100644 --- a/src/test/stdtest/float.rs +++ b/src/test/stdtest/float.rs @@ -47,7 +47,7 @@ fn test_nonpositive() { assert(float::nonpositive(-1.)); assert(float::nonpositive(float::neg_infinity())); assert(float::nonpositive(1./float::neg_infinity())); - // TODO: assert(!float::nonpositive(float::NaN())); + assert(!float::nonpositive(float::NaN())); } #[test] @@ -58,5 +58,5 @@ fn test_nonnegative() { assert(!float::nonnegative(-1.)); assert(!float::nonnegative(float::neg_infinity())); assert(!float::nonnegative(1./float::neg_infinity())); - // TODO: assert(!float::nonnegative(float::NaN())); + assert(!float::nonnegative(float::NaN())); } From 1f5e47e907db0a4c8f39f99a424cb2623fb37d9d Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 28 Oct 2011 12:56:54 -0700 Subject: [PATCH 4/5] Add more isNaN tests --- src/test/run-pass/float-nan.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/run-pass/float-nan.rs b/src/test/run-pass/float-nan.rs index 61ef4373a8c2a..d0ec3d4f9949c 100644 --- a/src/test/run-pass/float-nan.rs +++ b/src/test/run-pass/float-nan.rs @@ -71,4 +71,12 @@ fn main() { 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)); } From f0a9fab8041ea54aa111f6fd38363ceb956cadc1 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 28 Oct 2011 14:20:10 -0700 Subject: [PATCH 5/5] Cleanup: Remove uneccesary generic_cmp function. --- src/comp/middle/trans.rs | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 8b2f57baf60d5..4c2abd17f36f4 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -1536,63 +1536,49 @@ 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, op: ast::binop) -> ValueRef { - let cmp = alt nt { + alt nt { nil_type. { // We don't need to do actual comparisons for nil. // () == () holds but () < () does not. alt op { - ast::eq. | ast::le. | ast::ge. { 1u } - ast::ne. | ast::lt. | ast::gt. { 0u } + ast::eq. | ast::le. | ast::ge. { ret C_bool(true); } + ast::ne. | ast::lt. | ast::gt. { ret C_bool(false); } } } floating_point. { - alt op { + 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. { - alt op { + 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. { - alt op { + 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); } - }; - // 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; - } - ret generic_cmp(cx, nt, cmp, lhs, rhs); + } } type val_pair_fn = fn(@block_ctxt, ValueRef, ValueRef) -> @block_ctxt;