Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(NaN == x) and (NaN < x) and (x < NaN) are true for all x:float #1083

Closed
mbrubeck opened this issue Oct 28, 2011 · 5 comments
Closed

(NaN == x) and (NaN < x) and (x < NaN) are true for all x:float #1083

mbrubeck opened this issue Oct 28, 2011 · 5 comments

Comments

@mbrubeck
Copy link
Contributor

NaN should not be less than, equal to, or greater than any number. But in Rust, float::NaN < 0.0 evaluates to true.

In fact, all of the following assertions pass:

assert(float::NaN() == float::infinity());
assert(float::NaN() == float::neg_infinity());
assert(float::NaN() == 0.);
assert(float::NaN() < 0.);
@pcwalton
Copy link
Contributor

This is by design. We break IEEE 754 compatibility because < and == are supposed to be used in hashtables and trees and such, and I'd rather not have hashtables mysteriously break when NaNs are used as keys.

We should have an IEEE 754-compliant comparison function in the standard library though.

@mbrubeck
Copy link
Contributor Author

I discussed this with pcwalton on IRC, and determined that (NaN < 0) is by design, but (NaN == Infinity) and (NaN == -Infinity) are bugs. Updated the issue description.

@mbrubeck
Copy link
Contributor Author

All of the assertions in the following program pass in the current Rust implementation.

use std;
import std::float;
fn main() {
  let nan = float::NaN();
  let inf = float::infinity();
  assert(-inf == float::neg_infinity());

  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 ==  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 <   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);
}

It appears that NaN is less than all values and all values are less than NaN; NaN is greater than no values and no values are greater than NaN; NaN is equal to all values and all values are equal to NaN.

Another quirk is that (inf - inf) is printed as -nan instead of nan.

@mbrubeck
Copy link
Contributor Author

The problem is that Rust uses ule|ult|ueq in its fcmp operations. According to the semantics, all of these operations yield true if either operand is QNaN.

compare_scalar_values uses LE, LT, and EQ operations directly for <=, <, and == expressions. It uses the LE, LT, and EQ operations and negates them for >, >=, and != expressions. This is why it the first three operations always return true for NaN, while the latter three always return false.

To get IEEE 754 semantics, we'd need to change it to use all six operations (ole|olt|oge|ogt|oeq) directly.

To get consistent non-IEEE 754 semantics, we'd first need to define those semantics, and then possibly add some special-case branches to handle QNaNs.

@mbrubeck
Copy link
Contributor Author

Fixed by #1090.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants