3

In C, on a implementation with IEEE-754 floats, when I compare two floating point numbers which are NaN, it return 0 or "false". But why do two floating point numbers which both are inf count as equal?

This Program prints "equal: ..." (at least under Linux AMD64 with gcc) and in my opinion it should print "different: ...".

#include <stdio.h>
#include <stdlib.h>

int main(void)
  {
    volatile double a = 1e200; //use volatile to suppress compiler warnings
    volatile double b = 3e200;
    volatile double c = 1e200;
    double resA = a * c;  //resA and resB should by inf
    double resB = b * c;
    if (resA == resB)
      {   
        printf("equal: %e * %e = %e = %e = %e * %e\n",a,c,resA,resB,b,c);
      }   
    else
      {   
        printf("different: %e * %e = %e != %e = %e * %e\n", a, c, resA, resB, b, c);
      }   
    return EXIT_SUCCESS;
  }

A other example, why I think inf is not the same as inf, is: the numbers of natural numbers and rational numbers, both are infinite but not the same.

So why is inf == inf?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Consider this code does not certainly compare 2 `double` that both are `infinity`. This code compares the results of 2 operations for equality. C does allow intermediate calculations to operate using a wider type. In that case, I would expect `a*c==b*c` to be false. A more direct sample code would use `volatile double` objects that are infinity and then compare those. – chux - Reinstate Monica Jan 24 '17 at 18:15
  • 1
    @chux thanks, i edit the source code – 12431234123412341234123 Jan 24 '17 at 19:30

4 Answers4

7

Infinities compare equal because that's what the standard says. From section 5.11 Details of comparison predicates:

Infinite operands of the same sign shall compare equal.

nwellnhof
  • 32,319
  • 7
  • 89
  • 113
2

inf==inf for the same reason that almost all floating point numbers compare equal to themselves: Because they're equal. They contain the same sign, exponent, and mantissa.

You might be thinking of how NaN != NaN. But that's a relatively unimportant consequence of a much more important invariant: NaN != x for any x. As the name implies, NaN is not any number at all, and hence cannot compare equal to anything, because the comparison in question is a numeric one (hence why -0 == +0).

It would certainly make some amount of sense to have inf compare unequal to other infs, since in a mathematical context they're almost certainly unequal. But keep in mind that floating point equality is not the same thing as absolute mathematical equality; 0.1f * 10.0f != 1.0f, and 1e100f + 1.0f == 1e100f. Just as floating point numbers gradually underflow into denormals without compromising as-good-as-possible equality, so they overflow into infinity without compromising as-good-as-possible equality.

If you want inf != inf, you can emulate it: 1e400 == 3e400 evaluates to true, but 1e400 - 3e400 == 0 evaluates to false, because the result of +inf + -inf is NaN. (Arguably you could say it should evaluate to 0, but that would serve nobody's interest.)

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • "not any number at all" inf is also not a number. – 12431234123412341234123 Jan 24 '17 at 21:06
  • 1
    "Infinity" the concept is not a number in the real number system. But `inf` the IEEE-754 value is indeed a number in IEEE-754. – Sneftel Jan 24 '17 at 21:16
  • 1
    FWIW, the C spec uses _floating types_ (`float`, `double`, ...): _normalized floating-point numbers_, _subnormal floating-point numbers_, _unnormalized floating-point numbers_, and values that are not floating-point numbers, such as infinities and NaNs. – chux - Reinstate Monica Jan 24 '17 at 21:21
  • @chux Good point. I should say, `inf` is far more number-ish than `NaN`. – Sneftel Jan 24 '17 at 21:32
  • @chux Though I'd further point out that they define "floating-point number" as "a finite *or infinite* number that is representable in a floating-point format". (How any "infinite number" is representable in a floating-point format... is a question for the philosophers.) – Sneftel Jan 24 '17 at 21:37
  • 1
    (754, that is, not C.) – Sneftel Jan 24 '17 at 21:44
1

Background

In C, according to the IEEE 754 binary floating point standard (so, if you use a float or a double) you're going to get an exact value that can be compared exactly with another variable of the same type. Well, this is true unless your computations result in a value that lies outside the range of integers that can be represented (i.e., overflow).

Why is Infinity == Infinity

resA and resB The IEEE-754 standard tailored the values of infinity and negative infinity to be greater than or less than, respectively, all other values that may be represented according to the standard (<= INFINITY == 0 11111111111 0000000000000000000000000000000000000000000000000000 and >= -INFINITY == 1 11111111111 0000000000000000000000000000000000000000000000000000), except for NaN, which is neither less than, equal to, or greater than any floating point value (even itself). Take note that infinity and it's negative have explicit definitions in their sign, exponent, and mantissa bits.

So, resA and resB are infinity and since infinity is explicitly defined and reproducible, resA==resB. I'm fairly certain this is how isinf() is implemented.

Why is NaN != NaN

However, NaN is not explicitly defined. A NaN value has a sign bit of 0, exponent bits of all 1s (just like infinity and it's negative), and any set of non-zero fraction bits (Source). So, how would you tell one NaN from another, if their fraction bits are arbitrary anyways? Well, the standard doesn't assume that and simply returns false when two floating point values of this structure are compared to one another.

More Explanation

Because infinity is an explicitly defined value (Source, GNU C Manual):

Infinities propagate through calculations as one would expect

2 + ∞ = ∞

4 ÷ ∞ = 0

arctan (∞) = π/2.

However, NaN may or may not propagate through propagate through computations. When it does, it is a QNan (Quieting NaN, most significant fraction bit set) and all computations will result in NaN. When it doesn't, it is a SNan (Signalling NaN, most significant fraction bit not set) and all computations will result in an error.

Community
  • 1
  • 1
Vladislav Martin
  • 1,504
  • 2
  • 15
  • 34
  • 1
    `NAN=0x7ff0000000000001` (as well as other values), but `NAN != NAN`. And `0x8000000000000000 == 0x0000000000000000` in a floating-point context. It's not as simple as comparing the bits (though it is *almost* as simple as that). – Sneftel Jan 24 '17 at 20:45
  • Oh, it doesn't. I was just pointing out that equality of the "special" values, like `inf` and `NaN` and `-0`, is down to individual decisions made by the IEEE committee, not hard-and-fast mathematical rules. (Well, except `-0==0`, that's pretty important, but the existence of -0 isn't.) If they'd gone the other way and made `inf != inf`, no airplanes would have fallen out of the sky. – Sneftel Jan 24 '17 at 20:53
  • @Sneftel That is a fair point, just wanted to make sure I understood where you were coming from. As far as the disparity between IEEE committee decisions and hard mathematical rules, one of the committee members put up a really long post about that [here](http://stackoverflow.com/a/1573715/5209610). – Vladislav Martin Jan 24 '17 at 20:56
  • Just saw your earlier addendum. It seems unlikely that any compiler would produce a warning without `volatile` but wouldn't produce one with one (`volatile` on a local variable whose address is never taken is as useless as applying `register`), but it's entirely possible I'm wrong. – Sneftel Jan 24 '17 at 20:58
  • 1
    Declaring the objects `volatile` was certainly to prevent that optimizations did not occur like wider math (`long double`) as originally posted - but did not. By assigned the _infinity_ to `resA, resB`, that issue is side-lined. – chux - Reinstate Monica Jan 24 '17 at 21:18
  • 1
    @chux I'm not sure how to verify that these optimizations occur when compiling the original posting. Is what you're saying that in order to perform `a*b` or `b*c`, the C99 compiler will reinterpret `a`, `b`, and `c` as as `long double`s? – Vladislav Martin Jan 24 '17 at 21:33
  • 1
    A perfectly compliant C99 compiler shouldn't, but real-world compilers have been known to keep values in extended-precision registers longer than they technically should. `volatile` is one potential tool for whipping the code generator into shape. (I don't think that matters in this case, though, since AFAIK extended-precision FPUs don't extend the exponent, only the mantissa.) – Sneftel Jan 24 '17 at 21:52
  • @VladislavMartin `a`, `b`, .... will be treated as `double`. The calculation of the product `a*c` could be done by first promoting to `long double`, and multiplying. The original `a*c==b*c` could have been a `long double` compare. OP has edited to `double resA = a*c;` which forces the retrieval of `reaA, reaB` as `double`. Should the code compare `==` as `double` or `long double` is not an issue as code is comparing infinity and infinity. So we do not need to verify the optimization did not occur due the `reaA, reaB ` assignments. IAC, a moot point now. – chux - Reinstate Monica Jan 24 '17 at 21:54
  • @VladislavMartin: *A NaN value has a sign bit of `0`*... Can't a NaN value have a sign bit of `1`? – Kushagr Jaiswal Sep 28 '21 at 01:38
0

There are many arithmetic systems. Some of them, including the ones normally covered in high school mathematics, such as the real numbers, do not have infinity as a number. Others have a single infinity, for example the projectively extended real line. Others, such as the IEEE floating point arithmetic under discussion, and the extended real line, have both positive and negative infinity.

IEEE754 arithmetic is different from real number arithmetic in many ways, but is a useful approximation for many purposes.

There is logic to the different treatment of NaNs and infinities. It is entirely reasonable to say that positive infinity is greater than negative infinity and any finite number. It would not be reasonable to say anything similar about the square root of -1.

Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75