Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: doubles, precision, virtual machines and GCC

Tags:

I have the following piece of code:

#include <cstdio>
int main()
{
   if ((1.0 + 0.1) != (1.0 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}

When compiled with O3 using gcc (4.4,4.5 and 4.6) and run natively (ubuntu 10.10), it prints the expected result of "equal".

However the same code when compiled as described above and run on a virtual machine (ubuntu 10.10, virtualbox image), it outputs "not equal" - this is the case for when the O3 and O2 flags are set however not O1 and below. When compiled with clang (O3 and O2) and run upon the virtual machine I get the correct result.

I understand 1.1 can't be correctly represented using double and I've read "What Every Computer Scientist Should Know About Floating-Point Arithmetic" so please don't point me there, this seems to be some kind of optimisation that GCC does that somehow doesn't seem to work in virtual machines.

Any ideas?

Note: The C++ standard says type promotion in this situations is implementation dependent, could it be that GCC is using a more precise internal representation that when the inequality test is applied holds true - due to the extra precision?

UPDATE1: The following modification of the above piece of code, now results in the correct result. It seems at some point, for whatever reason, GCC turns off floating point control word.

#include <cstdio>
void set_dpfpu() { unsigned int mode = 0x27F; asm ("fldcw %0" : : "m" (*&mode)); 
int main()
{
   set_dpfpu();
   if ((1.0 + 0.1) != (1.0 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}

UPDATE2: For those asking about the const expression nature of the code, I've changed it as follows, and still fails when compiled with GCC. - but i assume the optimizer may be turning the following into a const expression too.

#include <cstdio>
void set_dpfpu() { unsigned int mode = 0x27F; asm ("fldcw %0" : : "m" (*&mode)); 
int main()
{
   //set_dpfpu();  uncomment to make it work.
   double d1 = 1.0;
   double d2 = 1.0;  
   if ((d1 + 0.1) != (d2 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}

UPDATE3 Resolution: Upgrading virtualbox to version 4.1.8r75467 resolved the issue. However the their remains one issue, that is: why was the clang build working.

like image 756
Jared Krumsie Avatar asked Jan 18 '12 19:01

Jared Krumsie


1 Answers

UPDATE: See this posting How to deal with excess precision in floating-point computations? It address the issues of extended floating point precision. I forgot about the extended precision in x86. I remember a simulation that should have been deterministic, but gave different results on Intel CPUs than on PowePC CPUs. The causes was Intel's extended precision architecture.

This Web page talks about how to throw Intel CPUs into double-precision rounding mode: http://www.network-theory.co.uk/docs/gccintro/gccintro_70.html.


Does virtualbox guarantee that its floating point operations are identical to the the hardware's floating point operations? I could not find a guarantee like that with a quick Google search. I also did not find a promise that vituralbox FP ops conform to IEEE 754.

VMs are emulators that try-- and mostly succeed-- to the emulate a particular instruction set or architecture. They are just emulators, however, and subject to their own implementation quirks or design issues.

If you haven't already, post the question forums.virtualbox.org and see what the community says about it.

like image 94
ahoffer Avatar answered Sep 28 '22 03:09

ahoffer