Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 284
SpaceWumpus Avatar asked Jan 16 '20 11:01

SpaceWumpus


People also ask

What are the limitations of floating point representation?

As a result, they do not represent all of the same values, are not binary compatible, and have different associated error rates. Because of a lack of guarantees on the specifics of the underlying floating-point system, no assumptions can be made about either precision or range.

Why floating point numbers should not be used for equality in expression?

This is because floating point values are not precise, and small rounding errors in the floating point operands may cause unexpected results.

Why do floating point errors occur?

It's a problem caused when the internal representation of floating-point numbers, which uses a fixed number of binary digits to represent a decimal number. It is difficult to represent some decimal number in binary, so in many cases, it leads to small roundoff errors.

How does floating point precision work?

A float has 23 bits of mantissa, and 2^23 is 8,388,608. 23 bits let you store all 6 digit numbers or lower, and most of the 7 digit numbers. This means that floating point numbers have between 6 and 7 digits of precision, regardless of exponent.


1 Answers

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).

like image 179
Eric Postpischil Avatar answered Sep 19 '22 12:09

Eric Postpischil