3

I've found an interesting case where the same C++ code yields different results on different system.

#include <cstdio>
int main()
{
    int a=20, b=14;
    if(a*1.0/b*(a+1)/(b+1)==2) printf("YES!");
    else printf("NO!");
}

Compiled on Ubuntu Linux 12.04 using GCC 4.6.3 it outputs YES!

Compiled on Windows 7 using GCC 4.6.2 it outputs NO!

However, using:

double  c = a*1.0/b*(a+1)/(b+1);
if (c==2) printf("YES!");
...

will return YES! on both machines.

Any ideas why this difference emerges? Is this caused by compiler version mismatch (pathlevel version number shouldn't matter THAT much)? And why does it actually output NO! on the Windows machine, while this condition is obviously true?

rafalcieslak
  • 915
  • 1
  • 12
  • 25

5 Answers5

14

Because you are doing an equality comparison on floating-point types, which in general should not be relied upon for particular bit-exact behaviour from machine to machine (or from compiler to compiler, etc.).

Possible reasons include the compiler's choice of when to move floating-point results out of wide (80-bit) floating-point registers (neither the language standard nor the IEEE-754 floating-point standard impose any particular requirements, AFAIK).

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 6
    I think it would be interesting to know what attribute of the compiler or processor was responsible for the difference. IEEE floating-point should be consistent if not exact. – Mark Ransom May 09 '12 at 15:43
  • @MarkRansom: There is no guarantee on consistency as the numbers are not exact. Re-arranging the order of operations (that should mathematically be consistent) can produce different results just because of the accuracy of the values involved. The compiler only guarantees mathematical consistency not accuracy consistency. – Martin York May 09 '12 at 17:57
  • @LokiAstari, gcc has a compiler switch to enable rearrangement of math operations when the result can't be guaranteed to match. – Mark Ransom May 09 '12 at 18:42
12

This is only a guess, you would need to look at the assembly output from the compiler to know for sure.

It is possible that one compiler left intermediate results in a floating-point register while the other wrote the results to memory, rounding it from 80 bits to 64. It's also possible that one uses SSE and the other does not.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    +1 (almost) everyone knows about FP issues but you tried to guess a **reason** for this difference! – Adriano Repetti May 09 '12 at 15:49
  • Yes, exactly, thanks for suggesting a reason for this issue :-) – rafalcieslak May 09 '12 at 15:52
  • 2
    To clarify, 20/14 can be represented exactly using 80bit precision but not with 32 bit precision (not tried 64). The Windows build is storing intermediate values in RAM or using SSE whereas the Ubuntu build is keeping intermediate values in the FPU. – Skizz May 09 '12 at 15:55
  • @Skizz, how is it possible that 80 bits would help? The fact that 14 isn't a power of 2 means the fraction will repeat infinitely. – Mark Ransom May 09 '12 at 15:59
  • @Skizz: 20/14 has a non-terminating expansion in base-2. – Oliver Charlesworth May 09 '12 at 16:01
  • @OliCharlesworth: You're right. The value is exact to 81bits (i.e. the value wasn't rounded up when stored at 80bits) but not exact at 33bits (i.e. it gets rounded up when stored at 32bits) – Skizz May 09 '12 at 16:13
  • 1
    @Skizz, it's never exact no matter how many bits you use. I'm guessing that you mean rounded down vs. rounded up. – Mark Ransom May 09 '12 at 16:15
2

it is because of floating point arithmetic, try using epsilon comparisions instead:

#define EPSILON_EQUAL(a,b) ( fabs((a)-(b)) < (0.0001f) )

float f = a*1.0/b*(a+1)/(b+1);
if(EPSILON_EQUAL(f,2.0f)) printf("YES!");
marcinj
  • 48,511
  • 9
  • 79
  • 100
  • Thanks for suggesting epsilon comparison, even if your answer does not explain why did this problem happen ;) – rafalcieslak May 09 '12 at 15:55
  • I suppose this has something to do with the fact that in first example you are using float ( in if statement ), in second example you use double. Here is SO that looks similar to your question: http://stackoverflow.com/questions/7702177/error-due-to-limited-precision-of-float-and-double – marcinj May 09 '12 at 16:16
0

because it is not correct to compare floating point numbers like that due to precision problems.

In mathematics 2/3= (0.6666666..... to infinity)//primary school math :) no questions asked.

In computing this calculation is carried out on the floating point unit (similar to CPU but is dedicated for floating point calculations). Now this floating point unit (FPU) may give you a number very close to your actual answer but they are not exactly the same. because it will truncate the results. There is an entire field dedicated to floating point arithmetic. In short never use floating point numbers in comparisons because you may get conflicting results.

0

Testing for equality between floating point numbers is problematic. This is because of rounding errors and there are many, MANY texts on this on the internet.

You can see this here, here, here, here, here and basically any result in a google search for floating point equality.

Community
  • 1
  • 1
Shahbaz
  • 46,337
  • 19
  • 116
  • 182