I know that using ==
to check equality of floating-point variables is not a good way. But I just want to know that with the following statements:
It won't be true if x
is NaN
, since comparisons on NaN
are always false (yes, even NaN == NaN
). For all other cases (normal values, subnormal values, infinities, zeros) this assertion will be true.
The advice for avoiding ==
for floats applies to calculations due to floating point numbers being unable to express many results exactly when used in arithmetic expressions. Assignment is not a calculation and there's no reason that assignment would yield a different value than the original.
Extended-precision evaluation should be a non-issue if the standard is followed. From <cfloat>
inherited from C [5.2.4.2.2.8] (emphasis mine):
Except for assignment and cast (which remove all extra range and precision), the values of operations with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type.
However, as the comments have pointed out, some cases with certain compilers, build-options, and targets could make this paradoxically false.
Yes, in all cases (disregarding NaNs and x87 issues), this will be true.
If you do a memcmp
on them you will be able test for equality while being able to compare NaNs and sNaNs. This will also require the compiler to take the address of the variable which will coerce the value into a 32-bit float
instead of an 80-bit one. This will eliminate the x87 issues. The second assertion here is intended to fail to show that ==
will not compare NaNs as true:
#include <cmath>
#include <cassert>
#include <cstring>
int main(void)
{
float x = std::nan("");
float y = x;
assert(!std::memcmp(&y, &x, sizeof(float)));
assert(y == x);
return 0;
}
Note that if the NaNs have a different internal representation (i.e. differing mantissa), the memcmp
will not compare true.
In usual cases, it would evaluate to true. (or the assert statement won't do anything)
Edit:
By 'usual cases' I mean am excluding the aforementioned scenarios (such as NaN values and 80x87 floating point units) as pointed by other users.
Given the obsolesence of 8087 chips in today's context, the issue is rather isolated and for the question to be applicable in current state of floating-point architecture used, its true for all cases except for NaNs.
(reference about 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm)
Kudos to @chtz for reproducing a good example and @kmdreko for mentioning NaNs - didn't knew about them before!
Besides the assert(NaN==NaN);
case pointed out by kmdreko, you can have situations with x87-math, when 80bit floats are temporarily stored to memory and later compared to values which are still stored inside a register.
Possible minimal example, which fails with gcc9.2 when compiled with -O2 -m32
:
#include <cassert>
int main(int argc, char**){
float x = 1.f/(argc+2);
volatile float y = x;
assert(x==y);
}
Godbolt Demo: https://godbolt.org/z/X-Xt4R
The volatile
can probably be omitted, if you manage to create sufficient register-pressure to have y
stored and reloaded from memory (but confuse the compiler enough, not to omit the comparison all-together).
See GCC FAQ reference:
Yes, y
will assuredly take on the value of x
:
[expr.ass]/2: In simple assignment (=), the object referred to by the left operand is modified ([defns.access]) by replacing its value with the result of the right operand.
There is no leeway for other values to be assigned.
(Others have already pointed out that an equivalence comparison ==
will nonetheless evaluate to false
for NaN values.)
The usual issue with floating-point ==
is that it's easy to not have quite the value you think you do. Here, we know that the two values, whatever they are, are the same.