Why can the compiler not optimize floating point addition with 0?
IEEE 754 floating-point numbers have two zero values, one negative, one positive. When added together, the result is the positive one.
So id1(-0.f)
is 0.f
, not -0.f
.
Note that id1(-0.f) == -0.f
because 0.f == -0.f
.
Demo
Also, note that compiling with -ffast-math
in GCC does make the optimization and changes the result.
"I have four identity functions which do essentially nothing."
That's not true.
For floating-point numbers x + 1 - 1
is not equal x + 0
, it is equal (x + 1) - 1
. So if you have e.g. a very small x
then you will lose that very small portion in the x + 1
step, and the compiler can't know if that was your intent or not.
And in the case of x * 2 / 2
, the x * 2
might not be exact either, due to floating-point precision, so you have a similar case here, the compiler does not know if you for some reason want to change the value of x
in that manner.
So these would be equal:
float id0(float x) {
return x + (1. - 1.);
}
float id1(float x) {
return x + 0;
}
And these would be equal:
float id2(float x) {
return x * (2. / 2.);
}
float id3(float x) {
return x * 1;
}
The desired behavior could for sure be defined in another way. But as already mentioned by Nelfeal this optimization has to be explicitly activated using -ffast-math
Enable fast-math mode. This option lets the compiler make aggressive, potentially-lossy assumptions about floating-point math. These include:
- Floating-point math obeys regular algebraic rules for real numbers (e.g. + and * are associative, x/y == x * (1/y), and (a + b) * c == a * c + b * c),
- Operands to floating-point operations are not equal to NaN and Inf, and
- +0 and -0 are interchangeable.
fast-math
is for clang and gcc a collection of flags (here the one listed by clang):
- -fno-honor-infinities
- -fno-honor-nans
- -fno-math-errno
- -ffinite-math
- -fassociative-math
- -freciprocal-math
- -fno-signed-zeros
- -fno-trapping-math
- -ffp-contract=fast