Search code examples
c++floating-pointinequality

Can floating point equality and inequality tests be assumed to be consistent and repeatable?


Assume two floating point numbers x and y, neither of which are Nan.

Is it safe to assume that equality and inequality tests will:

a. Be consistent with each other: E.g. is the following guaranteed true: (x >= y) == ((x > y) || (x == y))

b. Be repeatable E.g. would (x == y) always give the same result every time it was evaluated, if neither x nor y have been changed. I ask this one because of the issue that the floating point unit can store intermediate results to a higher precision that they are stored in memory, and so the value of a variable could possibly change depending on whether it was the result of a recent calculation which is still in the FPU, or whether it has come from memory. Conceivably the first test could be in the former case, and a later test the latter.


Solution

  • Provided the x and y in the question are identifiers (rather than abbreviations for expressions generally, such as x standing for b + sqrt(c)), then the C++ standard requires (x >= y) == (x > y || x == y) to be true.

    C++ 2017 (draft N4659) 8 13 allows floating-point expressions to be evaluated with greater precision and range than required by their nominal types. For example, while evaluating an operator with float operands, the implementation may use double arithmetic. However, footnote 64 there refers us to 8.4, 8.2.9, and 8.18 to understand that the cast and assignment operators must perform their specific conversions, which produce a value representable in the nominal type.

    Thus, once x and y have been assigned values, there is no excess precision, and they do not have different values in different uses. Then (x >= y) == (x > y || x == y) must be true because it is evaluated as it appears and is necessarily mathematically true.

    The existence of GCC bug 323 means you cannot rely on GCC when compiling for i386, but this is due to a bug in GCC which violates the C++ standard. Standard C++ does not permit this.

    If comparisons are made between expressions, as in:

    double y = b + sqrt(c);
    if (y != b + sqrt(c))
        std::cout << "Unequal\n";
    

    then the value assigned to y may differ from the value computed for the right operator of b + sqrt(c), and the string may be printed, because b + sqrt(c) may have excess precision, whereas y must not.

    Since casts are also required to remove excess precision, then y != (double) (b + sqrt(c)) should always be false (given the definition of y above).