Skip to content

Commit f44c8e8

Browse files
committed
Use IEEE 754 semantics for NaN (Issue rust-lang#1084)
1 parent 17ea058 commit f44c8e8

File tree

4 files changed

+149
-59
lines changed

4 files changed

+149
-59
lines changed

src/comp/middle/trans.rs

Lines changed: 47 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,26 +1492,26 @@ tag scalar_type { nil_type; signed_int; unsigned_int; floating_point; }
14921492

14931493

14941494
fn compare_scalar_types(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
1495-
t: ty::t, llop: ValueRef) -> result {
1496-
let f = bind compare_scalar_values(cx, lhs, rhs, _, llop);
1495+
t: ty::t, op: ast::binop) -> result {
1496+
let f = bind compare_scalar_values(cx, lhs, rhs, _, op);
14971497

14981498
alt ty::struct(bcx_tcx(cx), t) {
1499-
ty::ty_nil. { ret f(nil_type); }
1499+
ty::ty_nil. { ret rslt(cx, f(nil_type)); }
15001500
ty::ty_bool. | ty::ty_uint. | ty::ty_ptr(_) | ty::ty_char. {
1501-
ret f(unsigned_int);
1501+
ret rslt(cx, f(unsigned_int));
15021502
}
1503-
ty::ty_int. { ret f(signed_int); }
1504-
ty::ty_float. { ret f(floating_point); }
1503+
ty::ty_int. { ret rslt(cx, f(signed_int)); }
1504+
ty::ty_float. { ret rslt(cx, f(floating_point)); }
15051505
ty::ty_machine(_) {
15061506
if ty::type_is_fp(bcx_tcx(cx), t) {
15071507
// Floating point machine types
1508-
ret f(floating_point);
1508+
ret rslt(cx, f(floating_point));
15091509
} else if ty::type_is_signed(bcx_tcx(cx), t) {
15101510
// Signed, integral machine types
1511-
ret f(signed_int);
1511+
ret rslt(cx, f(signed_int));
15121512
} else {
15131513
// Unsigned, integral machine types
1514-
ret f(unsigned_int);
1514+
ret rslt(cx, f(unsigned_int));
15151515
}
15161516
}
15171517
ty::ty_type. {
@@ -1535,34 +1535,47 @@ fn compare_scalar_types(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
15351535

15361536
// A helper function to do the actual comparison of scalar values.
15371537
fn compare_scalar_values(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
1538-
nt: scalar_type, llop: ValueRef) -> result {
1539-
let eq_cmp;
1540-
let lt_cmp;
1541-
let le_cmp;
1542-
alt nt {
1538+
nt: scalar_type, op: ast::binop) -> ValueRef {
1539+
let cmp = alt nt {
15431540
nil_type. {
15441541
// We don't need to do actual comparisons for nil.
15451542
// () == () holds but () < () does not.
1546-
eq_cmp = 1u;
1547-
lt_cmp = 0u;
1548-
le_cmp = 1u;
1543+
alt op {
1544+
ast::eq. | ast::le. | ast::ge. { 1u }
1545+
ast::ne. | ast::lt. | ast::gt. { 0u }
1546+
}
15491547
}
15501548
floating_point. {
1551-
eq_cmp = lib::llvm::LLVMRealUEQ;
1552-
lt_cmp = lib::llvm::LLVMRealULT;
1553-
le_cmp = lib::llvm::LLVMRealULE;
1549+
alt op {
1550+
ast::eq. { lib::llvm::LLVMRealOEQ }
1551+
ast::ne. { lib::llvm::LLVMRealUNE }
1552+
ast::lt. { lib::llvm::LLVMRealOLT }
1553+
ast::le. { lib::llvm::LLVMRealOLE }
1554+
ast::gt. { lib::llvm::LLVMRealOGT }
1555+
ast::ge. { lib::llvm::LLVMRealOGE }
1556+
}
15541557
}
15551558
signed_int. {
1556-
eq_cmp = lib::llvm::LLVMIntEQ;
1557-
lt_cmp = lib::llvm::LLVMIntSLT;
1558-
le_cmp = lib::llvm::LLVMIntSLE;
1559+
alt op {
1560+
ast::eq. { lib::llvm::LLVMIntEQ }
1561+
ast::ne. { lib::llvm::LLVMIntNE }
1562+
ast::lt. { lib::llvm::LLVMIntSLT }
1563+
ast::le. { lib::llvm::LLVMIntSLE }
1564+
ast::gt. { lib::llvm::LLVMIntSGT }
1565+
ast::ge. { lib::llvm::LLVMIntSGE }
1566+
}
15591567
}
15601568
unsigned_int. {
1561-
eq_cmp = lib::llvm::LLVMIntEQ;
1562-
lt_cmp = lib::llvm::LLVMIntULT;
1563-
le_cmp = lib::llvm::LLVMIntULE;
1569+
alt op {
1570+
ast::eq. { lib::llvm::LLVMIntEQ }
1571+
ast::ne. { lib::llvm::LLVMIntNE }
1572+
ast::lt. { lib::llvm::LLVMIntULT }
1573+
ast::le. { lib::llvm::LLVMIntULE }
1574+
ast::gt. { lib::llvm::LLVMIntUGT }
1575+
ast::ge. { lib::llvm::LLVMIntUGE }
1576+
}
15641577
}
1565-
}
1578+
};
15661579
// FIXME: This wouldn't be necessary if we could bind methods off of
15671580
// objects and therefore abstract over FCmp and ICmp (issue #435). Then
15681581
// 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,
15791592
} else { r = ICmp(cx, op, lhs, rhs); }
15801593
ret r;
15811594
}
1582-
let last_cx = new_sub_block_ctxt(cx, "last");
1583-
let eq_cx = new_sub_block_ctxt(cx, "eq");
1584-
let eq_result = generic_cmp(eq_cx, nt, eq_cmp, lhs, rhs);
1585-
Br(eq_cx, last_cx.llbb);
1586-
let lt_cx = new_sub_block_ctxt(cx, "lt");
1587-
let lt_result = generic_cmp(lt_cx, nt, lt_cmp, lhs, rhs);
1588-
Br(lt_cx, last_cx.llbb);
1589-
let le_cx = new_sub_block_ctxt(cx, "le");
1590-
let le_result = generic_cmp(le_cx, nt, le_cmp, lhs, rhs);
1591-
Br(le_cx, last_cx.llbb);
1592-
let unreach_cx = new_sub_block_ctxt(cx, "unreach");
1593-
Unreachable(unreach_cx);
1594-
let llswitch = Switch(cx, llop, unreach_cx.llbb, 3u);
1595-
AddCase(llswitch, C_u8(abi::cmp_glue_op_eq), eq_cx.llbb);
1596-
AddCase(llswitch, C_u8(abi::cmp_glue_op_lt), lt_cx.llbb);
1597-
AddCase(llswitch, C_u8(abi::cmp_glue_op_le), le_cx.llbb);
1598-
let last_result =
1599-
Phi(last_cx, T_i1(), [eq_result, lt_result, le_result],
1600-
[eq_cx.llbb, lt_cx.llbb, le_cx.llbb]);
1601-
ret rslt(last_cx, last_result);
1595+
ret generic_cmp(cx, nt, cmp, lhs, rhs);
16021596
}
16031597

16041598
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,
19121906
ret rslt(bcx, Load(bcx, llcmpresultptr));
19131907
}
19141908

1915-
// Compares two values. Performs the simple scalar comparison if the types are
1916-
// scalar and calls to comparison glue otherwise.
1917-
fn compare(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef, t: ty::t,
1918-
llop: ValueRef) -> result {
1919-
if ty::type_is_scalar(bcx_tcx(cx), t) {
1920-
ret compare_scalar_types(cx, lhs, rhs, t, llop);
1921-
}
1922-
ret call_cmp_glue(cx, lhs, rhs, t, llop);
1923-
}
1924-
19251909
fn take_ty(cx: @block_ctxt, v: ValueRef, t: ty::t) -> @block_ctxt {
19261910
if ty::type_has_pointers(bcx_tcx(cx), t) {
19271911
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,
22622246

22632247
fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
22642248
_lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result {
2249+
if ty::type_is_scalar(bcx_tcx(cx), rhs_t) {
2250+
let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op);
2251+
ret rslt(rs.bcx, rs.val);
2252+
}
2253+
22652254
// Determine the operation we need.
22662255
let llop;
22672256
alt op {
@@ -2270,7 +2259,7 @@ fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
22702259
ast::le. | ast::gt. { llop = C_u8(abi::cmp_glue_op_le); }
22712260
}
22722261

2273-
let rs = compare(cx, lhs, rhs, rhs_t, llop);
2262+
let rs = call_cmp_glue(cx, lhs, rhs, rhs_t, llop);
22742263

22752264
// Invert the result if necessary.
22762265
alt op {

src/lib/float.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ fn NaN() -> float {
213213
ret 0./0.;
214214
}
215215

216+
/* Predicate: isNaN */
217+
pure fn isNaN(f: float) -> bool { f != f }
218+
216219
/* Function: infinity */
217220
pure fn infinity() -> float {
218221
ret 1./0.;

src/test/run-pass/float-nan.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use std;
2+
import std::float;
3+
4+
fn main() {
5+
let nan = float::NaN();
6+
assert(float::isNaN(nan));
7+
8+
let inf = float::infinity();
9+
assert(-inf == float::neg_infinity());
10+
11+
assert( nan != nan);
12+
assert( nan != -nan);
13+
assert(-nan != -nan);
14+
assert(-nan != nan);
15+
16+
assert( nan != 1.);
17+
assert( nan != 0.);
18+
assert( nan != inf);
19+
assert( nan != -inf);
20+
21+
assert( 1. != nan);
22+
assert( 0. != nan);
23+
assert( inf != nan);
24+
assert(-inf != nan);
25+
26+
assert(!( nan == nan));
27+
assert(!( nan == -nan));
28+
assert(!( nan == 1.));
29+
assert(!( nan == 0.));
30+
assert(!( nan == inf));
31+
assert(!( nan == -inf));
32+
assert(!( 1. == nan));
33+
assert(!( 0. == nan));
34+
assert(!( inf == nan));
35+
assert(!(-inf == nan));
36+
assert(!(-nan == nan));
37+
assert(!(-nan == -nan));
38+
39+
assert(!( nan > nan));
40+
assert(!( nan > -nan));
41+
assert(!( nan > 0.));
42+
assert(!( nan > inf));
43+
assert(!( nan > -inf));
44+
assert(!( 0. > nan));
45+
assert(!( inf > nan));
46+
assert(!(-inf > nan));
47+
assert(!(-nan > nan));
48+
49+
assert(!(nan < 0.));
50+
assert(!(nan < 1.));
51+
assert(!(nan < -1.));
52+
assert(!(nan < inf));
53+
assert(!(nan < -inf));
54+
assert(!(nan < nan));
55+
assert(!(nan < -nan));
56+
57+
assert(!( 0. < nan));
58+
assert(!( 1. < nan));
59+
assert(!( -1. < nan));
60+
assert(!( inf < nan));
61+
assert(!(-inf < nan));
62+
assert(!(-nan < nan));
63+
64+
assert(float::isNaN(nan + inf));
65+
assert(float::isNaN(nan + -inf));
66+
assert(float::isNaN(nan + 0.));
67+
assert(float::isNaN(nan + 1.));
68+
assert(float::isNaN(nan * 1.));
69+
assert(float::isNaN(nan / 1.));
70+
assert(float::isNaN(nan / 0.));
71+
assert(float::isNaN(0. / 0.));
72+
assert(float::isNaN(-inf + inf));
73+
assert(float::isNaN(inf - inf));
74+
}

src/test/stdtest/float.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fn test_from_str() {
1010
assert ( float::from_str("2.5e10") == 25000000000. );
1111
assert ( float::from_str("25000000000.E-10") == 2.5 );
1212
assert ( float::from_str("") == 0. );
13-
assert ( float::from_str(" ") == 0. );
13+
assert ( float::isNaN(float::from_str(" ")) );
1414
assert ( float::from_str(".") == 0. );
1515
assert ( float::from_str("5.") == 5. );
1616
assert ( float::from_str(".5") == 0.5 );
@@ -25,6 +25,7 @@ fn test_positive() {
2525
assert(!float::positive(-1.));
2626
assert(!float::positive(float::neg_infinity()));
2727
assert(!float::positive(1./float::neg_infinity()));
28+
assert(!float::positive(float::NaN()));
2829
}
2930

3031
#[test]
@@ -35,4 +36,27 @@ fn test_negative() {
3536
assert(float::negative(-1.));
3637
assert(float::negative(float::neg_infinity()));
3738
assert(float::negative(1./float::neg_infinity()));
39+
assert(!float::negative(float::NaN()));
40+
}
41+
42+
#[test]
43+
fn test_nonpositive() {
44+
assert(!float::nonpositive(float::infinity()));
45+
assert(!float::nonpositive(1.));
46+
assert(!float::nonpositive(0.));
47+
assert(float::nonpositive(-1.));
48+
assert(float::nonpositive(float::neg_infinity()));
49+
assert(float::nonpositive(1./float::neg_infinity()));
50+
// TODO: assert(!float::nonpositive(float::NaN()));
51+
}
52+
53+
#[test]
54+
fn test_nonnegative() {
55+
assert(float::nonnegative(float::infinity()));
56+
assert(float::nonnegative(1.));
57+
assert(float::nonnegative(0.));
58+
assert(!float::nonnegative(-1.));
59+
assert(!float::nonnegative(float::neg_infinity()));
60+
assert(!float::nonnegative(1./float::neg_infinity()));
61+
// TODO: assert(!float::nonnegative(float::NaN()));
3862
}

0 commit comments

Comments
 (0)