Warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
HarjitSi opened this issue ยท 4 comments
Compiling with GCC using -Wfloat-equal turned on, I get the following warnings:
printf.c:346:18: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
printf.c:363:20: warning: comparing floating point with == or != is unsafe [-Wfloat-equal]
The code does has a compare to 0.5
Any ideas on how to resolve this?
Thank you for creating this wonderful function!
The simplest way would probably be to rewrite it to use something like:
// presuming diff is positive
if ( diff > 0.5 ) {
} else if ( !(diff < 0.5) ) {
// if not > and not <, then it must be ==
// (but this will also happen if diff is NaN)
}
I am not sure how to check for NaN
without including <math.h>
. So to get an actual comparison, I believe you would have to do something like this:
bool is_zero_point_five(double num)
{
// private union, used to get the
// bitwise representation of the float
union double_to_u32
{
uint64_t as_uint;
double as_float;
};
union double_to_u32 zero_point_five = { .as_float = 0.5 };
union double_to_u32 value = { .as_float = num };
return value.as_uint == zero_point_five.as_uint;;
}
Basically, this uses a union
to get an unsigned 64 bit representation of the double
, and then compares the integers.
This gives no warnings and produces rather simple code in gcc. With optimizations enabled, GCC simply compares the value to 0x3FE0000000000000
, which is the IEEE-754 representation of 0.5
:
// gcc ARM 64
is_zero_point_five:
fmov x1, d0
mov x0, 4602678819172646912
cmp x1, x0
cset w0, eq
ret
// gcc x86-64
is_zero_point_five:
movabs rax, 4602678819172646912
movq rdx, xmm0
cmp rdx, rax
sete al
ret
This can be generalized to:
static inline bool double_compare_exact(double a, double b)
{
union double_to_u32 { uint64_t as_uint; double as_float; };
union double_to_u32 aa = { .as_float = a };
union double_to_u32 bb = { .as_float = b };
return aa.as_uint == bb.as_uint;
}
Due to the fact that gcc must account for NaN
s and perhaps other floating point exceptions, it cannot optimize !(x < 0.5) && !(x > 0.5)
to the equivalent of x != 0.5
. However, although the union
implementation doesn't produce warnings, it presumes that sizeof(double)
is 8 (which should be true on practically all platforms).
Hello @HarjitSi,
thanks a lot for bringing this problem to attention.
I'm aware of this "bug" and I don't like it either. Comparing floats to discrete numbers is never a sure/good thing.
The whole ftoa
routine needs further testing and minor refactoring. This is still on the todo list.
Anyway, this is just a warning but if you need to be warning free, a construct of greater/smaller comparisons might help - as a quick fix.
@vgrudenic , thanks a lot for your suggestion.
Is double
really and always 8 bytes on any platform?
I have to investigate this a little further...
Merry christmas! ๐
Hi, merry christmas to you too! ๐๐
No, I believe the C standard says nothing about the size, although IEEE 754 / IEC 60559 specifies float and double as 32 and 64-bit.
Perhaps my comment was unnecessarily complicated, and the most reasonable option would be to suppress the warning in GCC? This should leave it to compiler to produce the correct code for a given platform, regardless of the size of floats:
// doesn't have to be a separate method
static inline bool double_are_equal(double a, double b)
{
// temporarily suppress "comparing float with == is unsafe"
// (we really want to check exactly against 0.5)
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return a == b;
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}