3

Below, the result1 and result2 variable values are reporting different values depending upon whether or not you compile the code with -g or with -O on GCC 4.2.1 and on GCC 3.2.0 (and I have not tried more recent GCC versions):

double double_identity(double in_double)
{
    return in_double;
}

...

double result1 = ceil(log(32.0) / log(2.0));
std::cout << __FILE__ << ":" << __LINE__ << ":" << "result1==" << result1 << std::endl;

double result2 = ceil(double_identity(log(32.0) / log(2.0)));
std::cout << __FILE__ << ":" << __LINE__ << ":" << "result2==" << result2 << std::endl;

result1 and result2 == 5 only when compiling using -g, but if instead I compile with -O I get result1 == 6 and result2 == 5.

This seems like a difference in how optimization is being done by the compiler, or something to do with IEEE floating point representation internally, but I am curious as to exactly how this difference occurs. I'm hoping to avoid looking at the assembler if at all possible.

The above was compiled in C++, but I presume the same would hold if it was converted to ANSI-C code using printfs.

The above discrepancy occurs on 32-bit Linux, but not on 64-bit Linux.

Thanks bg

bgoodr
  • 2,744
  • 1
  • 30
  • 51
  • The lesson you should learn from this: Don't use floating point log/pow/etc. functions (or any floating point) when you want to perform exact integer operations unless you have a PhD in numerical analysis. – R.. GitHub STOP HELPING ICE Jul 24 '10 at 09:36
  • Agreed. In my defense, the code snippet I used was a trimmed down replica of another developers code, and of course it broke, hence why I am here. – bgoodr Jul 24 '10 at 15:23

1 Answers1

6

On x86, with optimizations on, the results of subexpressions are not necessarily stored into a 64-bit memory location before being used as part of a larger expression.

Because x86's standard floating-point registers are 80 bits, this means that in such cases, extra precision is available. IF you then divide (or multiply) that especially-precise value by another, the effects of the increased precision can magnify to the point where they can be perceived by the naked eye.

Intel's 64-bit processors use SSE registers for floating point math, and those registers don't have the extra precision.

You can play around with g++ flags to fix this if you really care.

Community
  • 1
  • 1
Drew Hoskins
  • 4,168
  • 20
  • 23
  • I translate "where they can be perceived by the naked eye" to also be "where the values from those calculations can be held in a floating-point register of 80-bits in width, and then passed to the ceil function which dutifully detects the extra precision and returns the next greater integral value". – bgoodr Jul 24 '10 at 15:25
  • I'm marking this as the answer because your link to http://stackoverflow.com/questions/3234042/is-there-a-linux-g-equivalent-to-the-fpprecise-and-fpfast-flags-used-in-v led me down the path to http://stackoverflow.com/questions/3234042/is-there-a-linux-g-equivalent-to-the-fpprecise-and-fpfast-flags-used-in-v/3234075#3234075 and I tried that on the above code and it gave the results I wanted. Now of course I have a different problem in that I suspect there may be tons of code just like this in the originating source code that will now behave differently if I throw the -ffloat-store option. – bgoodr Jul 24 '10 at 15:36