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

Why OrderedFloat is not IEEE conformant? #36

Open
kryptan opened this issue Oct 29, 2017 · 3 comments
Open

Why OrderedFloat is not IEEE conformant? #36

kryptan opened this issue Oct 29, 2017 · 3 comments

Comments

@kryptan
Copy link

kryptan commented Oct 29, 2017

For OrderedFloat:

NaN is sorted as greater than all other values and equal to itself, in contradiction with the IEEE standard.

IEEE standard requires different NaNs to be not equal, positive NaN to be greater than +∞ and negative NaN to be less than -∞. It also requires -0 to be less than +0. Why non-conformant behavior was chosen? Wouldn't it make more sense to implement total order as defined in standard instead?

This Stack Overflow answer is presumably providing a fast way to implement IEEE total order comparison.

@1011X
Copy link

1011X commented Jan 20, 2019

So according to what I could find about the definition of total order provided by IEEE 754, part of it would require that negative zero and positive zero compare differently from the default implementation. In other words, -0.0 < +0.0 returns false by default, but for total order it would have to be true. It also describes how different representations of the same floating point number would be ordered, and how NaNs with different payloads would compare with each other.

This looks like a big problem because Rust's documentation for Ord and PartialOrd explicitly say:

Implementations of PartialEq, PartialOrd, and Ord must agree with each other.

If IEEE-conformant total order is implemented using Ord, then it would disagree with the implementation of PartialOrd for f32 and f64, likely violating assumptions made by the standard library. I'm not sure what the exact consequences of that would be, but they don't sound good.

The only way I can think of to avoid this would be to have a separate trait specifically for floating-point total order.

(Btw, this is all based on what I could find from the section I linked above. If I misunderstood something or got something wrong, feel free to correct me.)

@kryptan
Copy link
Author

kryptan commented Jan 21, 2019

If IEEE-conformant total order is implemented using Ord, then it would disagree with the implementation of PartialOrd for f32 and f64

If we would implement Ord for f32 and f64 in standard library then yes, this would be a problem and PartialOrd should be compatible with Ord. But this library instead provides wrappers for f32 and f64, implementation of Ord and PartialOrd for these wrappers can agree with each other regardless of what standard library does for f32 and f64.

This is definition of total order from the standard:

5.10 Details of totalOrder predicate

For each supported arithmetic format, an implementation shall provide the following predicate that defines an ordering among all operands in a particular format:

boolean totalOrder(source, source)

totalOrder(x, y) imposes a total ordering on canonical members of the format of x and y:

a) If x<y, totalOrder(x, y) is true.
b) If x>y, totalOrder(x, y) is false.
c) If x=y:

  1. totalOrder(−0, +0) is true.
  2. totalOrder(+0, −0) is false.
  3. If x and y represent the same floating-point datum:
    i) If x and y have negative sign, totalOrder(x, y) is true if and only if the exponent of x≥ the exponent of y
    ii) otherwise totalOrder(x, y) is true if and only if the exponent of x≤ the exponent of y.

d) If x and y are unordered numerically because x or y is NaN:

  1. totalOrder(−NaN, y) is true where -NaN represents a NaN with negative sign bit and y is a floating-point number.
  2. totalOrder(x, +NaN) is true where +NaN represents a NaN with positive sign bit and x is a floating-point number.
  3. If x and y are both NaNs, then totalOrder reflects a total ordering based on:
    i) negative sign orders below positive sign
    ii) signaling orders below quiet for +NaN, reverse for -NaN
    iii) lesser payload, when regarded as an integer, orders below greater payload for +NaN,reverse for -NaN.

Neither signaling NaNs nor quiet NaNs signal an exception. For canonical x and y, totalOrder(x, y) and totalOrder(y, x) are both true if x and y are bitwise identical.

NOTE—totalOrder does not impose a total ordering on all encodings in a format. In particular, it does not distinguish among different encodings of the same floating-point representation, as when one or both encodings are non-canonical.

@Diggsey
Copy link

Diggsey commented Aug 7, 2019

@kryptan because people (myself included) still expect these wrappers to behave like a number. The aim is to be as close as possible to how ordering works for the native float types, whilst still upholding the guarantees of Ord. If negative zero compared differently to positive zero it would be useless for me and many other people.

It may make sense to have another type that implements the IEEE-defined ordering, but I suspect it will see less usage...

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

3 participants