Difference between std::fabs(a * b) and std::fabs(a) * std::fabs(b)
I believe I have found a counter example. I post this as a separate answer, because I don't think that this is at all analogous to the case for integers.
In the cases I considered, I missed that it is possible to change the rounding mode for floating point arithmetic. Problematically, GCC seems to to ignore that when he (I guess) optimizes "known" quantities at compile time. Consider the following code:
#include <iostream>
#include <cmath>
#include <cfenv>
double fabsprod1(double a, double b) {
return std::fabs(a*b);
}
double fabsprod2(double a, double b) {
return std::fabs(a) * std::fabs(b);
}
int main() {
std::fesetround(FE_DOWNWARD);
double a = 0.1;
double b = -3;
std::cout << std::hexfloat;
std::cout << "fabsprod1(" << a << "," << b << "): " << fabsprod1(a,b) << "\n";
std::cout << "fabsprod2(" << a << "," << b << "): " << fabsprod2(a,b) << "\n";
#ifdef CIN
std::cin >> b;
#endif
}
Output differs, depending on whether I compile with
g++ -DCIN -O1 -march=native main2.cpp && ./a.out
or
g++ -O1 -march=native main2.cpp && ./a.out
Notably, it only takes O1 (what I would consider completely reliable) to change the output in a way that does not seem reasonable to me.
With -DCIN the output is
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333333p-2
without -DCIN the output is
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
Edit: Peter Cordes (thank you for the comment) pointed out, that this surprising result was due to my failure in telling GCC to respect the change of rounding mode. By building with the following command, the expected results are achieved:
g++ -O1 -frounding-math -march=native main2.cpp && ./a.out
(works with O2 and O3 as well on my machine).