1

This question is about the IEEE totalOrder predicate for floats. The algorithm is described in this answer, and also on page 42 of Swartzlander and Lemonds' Computer Arithmetic Volume III:

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

  1. If x < y, totalOrder(x, y) is true.
  2. If x > y, totalOrder(x, y) is false.
  3. 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:
      • If x and y have negative sign, totalOrder(x, y) is true if and only if the exponent of x ≥ the exponent of y.
      • Otherwise totalOrder(x, y) is true if and only if the exponent of x ≤ the exponent of y.
  4. If x and y are unordered numerically because x or y is NaN:
  5. totalOrder(−NaN, y) is true where −NaN represents a NaN with negative sign bit and y is a floating-point number.
  6. totalOrder(x, +NaN) is true where +NaN represents a NaN with positive sign bit and x is a floating-point number.
  7. If x and y are both NaNs, then totalOrder reflects a total ordering based on:
    • negative sign orders below positive sign
    • signaling orders below quiet for +NaN, reverse for −NaN
    • lesser payload, when regarded as an integer, orders below greater payload for +NaN, reverse for −NaN.

My question is: What does the bolded part mean? What is an example of two IEEE754 float bit-patterns where x = y and yet they have different exponents?

Actually the only two IEEE754 floats I can think of that are x = y and have different bit-patterns at all are −0 and +0. NaNs are never equal, and among normal and denormal numbers I'm not aware of any with multiple bit-patterns.


I found a possible (partial) answer at Apple's documentation for isCanonical:

On platforms that fully support IEEE 754, every Float or Double value is canonical, but non-canonical values can exist on other platforms or for other types. Some examples:

  • On platforms that flush subnormal numbers to zero (such as armv7 with the default floating-point environment), Swift interprets subnormal Float and Double values as non-canonical zeros. (In Swift 5.1 and earlier, isCanonical is true for these values, which is the incorrect value.)
  • On i386 and x86_64, Float80 has a number of non-canonical encodings. “Pseudo-NaNs”, “pseudo-infinities”, and “unnormals” are interpreted as non-canonical NaN encodings. “Pseudo-denormals” are interpreted as non-canonical encodings of subnormal values.
  • Decimal floating-point types admit a large number of non-canonical encodings.

However, if the answer is "Point 3.3 is specifically for the benefit of platforms that do not fully support IEEE754," isn't that kind of weird and circular? And also, taking the first case above (armv7 with all denormals considered =)... since all subnormal/denormal numbers have exactly the same exponent (namely 2−126), point 3.3 doesn't even help to distinguish between them!

So. What's the point/ramifications/intent of this point of the spec?

user3840170
  • 26,597
  • 4
  • 30
  • 62
Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
  • 2
    Short answer: decimal formats. For example, `20 × 10^-1` and `2 × 10^0` represent the same floating-point datum, but have different exponents. `20 × 10^-1` is less than `2 × 10^0` for the purposes of `totalOrder`. – Mark Dickinson Sep 06 '21 at 07:04
  • @MarkDickinson: Ah, I think I see. The IEEE754 _decimal_ floating-point format can't have an implicit leading digit (because base 10 has nine possible non-zero leading digits); therefore the leading digit is explicit; therefore it can be zero; therefore you can have "denormals" with any exponent you like. I _think_ your example is wrong in that you wouldn't have "`20 × 10^-1`" (the mantissa/significand is still always <10, right?), but replace that with "`0.2 × 10^1`" and you get the same issue. ...Anyway, this sounds like a good actual answer, if someone would write it up! :) – Quuxplusone Sep 06 '21 at 15:12
  • 1
    Whether the significand is always < 10 depends on the conventions you're using. IEEE 754 describes two possible conventions for the significand and exponent - you can either have an integer significand (and in that case the corresponding exponent is denoted `q` in the standard), or a significand with a notional decimal point placed after the first digit (in which case the corresponding exponent is denoted `e` in the standard). Both conventions are useful. The `totalOrder` definition doesn't specify which type of exponent it's thinking of, but it doesn't actually make a difference. – Mark Dickinson Sep 06 '21 at 15:22
  • @Quuxplusone "if someone would write it up" --> You can [too](https://stackoverflow.com/help/self-answer). – chux - Reinstate Monica Sep 07 '21 at 11:16

1 Answers1

0

Floating-point numbers may have multiple representations in decimal formats, such as 1.234•103 and 12.34•102, and NaN has multiple representations in binary and decimal formats.

A floating-point datum is “A floating-point number or non-number (NaN) that is representable in a floating-point format…” (IEEE 754 draft D2.47, March 2019, 2.1 10 and IEEE 754-2008 2.1.24). In Table 3.1 (clause 3.2 in both 754-2008 and the 2019 draft), we see four specification levels:

  1. Extended real numbers: {−∞ through +∞}.
  2. Floating-point data: {−∞ through −0} ∪ {+0 through +∞} ∪ NaN.
  3. Representations of floating-point data: (sign, exponent, significand) ∪ {−∞,+∞} ∪ qNaN ∪ sNaN.
  4. Bit strings.

Observe that a floating-point datum is a member of floating-point data. This clause also tells us:

A floating-point datum, which can be a signed zero, finite non-zero number, signed infinity, or a NaN (not-a-number), can be mapped to one or more representations of floating-point data in a format.

So there is just one floating-point datum that is a NaN, but it has many representations.

In clause 3.3, we find (last paragraph on page 8 in 2008, lines 27-28 in 2019):

Binary interchange formats have just one representation each for +0 and −0, but decimal formats have many.

Clause 3.4 tells us why binary formats have unique representations:

To make the encoding unique, in terms of the parameters in 3.3, the value of the significand m is maximized by decreasing e until either e=emin or m≥1.

Clause 3.5 discusses multiple representations in decimal formats:

Unlike in a binary floating-point format, in a decimal floating-point format a number might have multiple representations. The set of representations a floating-point number maps to is called the floating-point number’s cohort; the members of a cohort are distinct representations of the same floating-point number. For example, if c is a multiple of 10 and q is less than its maximum allowed value, then (s, q, c) and (s, q+1, c/10) are two representations for the same floating-point number and are members of the same cohort.

While numerically equal, different members of a cohort can be distinguished by the decimal-specific operations (see 5.3.2, 5.5.2, and 5.7.3)…

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312