2

I have the following code that is exhibiting some very strange behavior. does anyone have any idea why.

#include <iostream>

long global = 20001;

double foo() {return global / 1000.0;}

int main(int,char**) {
  if (foo() == foo()) {
    std::cout << "true\n";
  } else {
    std::cout << "false\n";
  }

  return 0;
}

This should print true as the function returns the same result each time but instead prints false;

This is built using g++ on Solaris 10. I don't know that the OS matter, but I haven't had a chance to try it on a different os

Sam Brinck
  • 891
  • 1
  • 7
  • 11
  • Try this: flost epsilon = 0.001f; if ( foo() - foo() < epsilon) .... – AdamF Jan 28 '14 at 19:15
  • 1
    I compiled your code and received a response of 'true'. I am running Ubuntu 12.04, with gcc 4.6.3. It looks like the OS may matter. – Kevin Jan 28 '14 at 19:16
  • I got 'true' on Fedora with g++ – Octopus Jan 28 '14 at 19:17
  • @Kevin With what compiler options? (Same thing for Mahmut.) – James Kanze Jan 28 '14 at 19:19
  • 2
    http://www.parashift.com/c++-faq/floating-point-arith.html http://www.parashift.com/c++-faq/floating-point-arith2.html – catscradle Jan 28 '14 at 19:21
  • extra info, sorry I didn't include it in the original question: os solaris 10 on x86 processor compiler: g++ 4.7.1 without any flags (g++ main.cpp; ./a.out) I tested on linux and the problem doesn't occur, so it must be solaris specific – Sam Brinck Jan 28 '14 at 19:31

2 Answers2

3

The OS probably doesn't matter, but the compiler and the architecture do. On an Intel, the usual convention is to return a floating point value in a floating point register, which has 64 bits precision (rather than the 53 of a double). And of course, the compiler does the arithmetic directly into that register, so you end up returning a value with 64 bits precision.

Of course, when the compiler generates code to call a function, it can't leave the value in a register, since the function might use that register. So it spills it to memory. As a double with 56 bits. The == operator compares this 56 bit value from the first call with the 64 bit value from the second, and finds that they aren't equal.

I might add that if the compiler inlines the functions, the problem will probably disappear.

All of which is perfectly legal according to the standard.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Sounds very reasonable, is there any way to make the comparison work without storing the variables locally? this is Code pulled from a much larger function where storing the values in locals isn't really an option – Sam Brinck Jan 28 '14 at 19:33
  • 1
    -ffloat-store seems to fix it by preventing the floating point comparison from happening on the register. Thanks, you sir are a genius – Sam Brinck Jan 28 '14 at 19:59
1

Likely a problem with doubles not evaluating to exactly the same value all the time (essentially rounding errors).

If you were to change your function to return integers, you would probably have the expected behaviour.

Very similar to this question: Deals with comparing floats

Try this:

#include <iostream>
#include <limits>

long   global = 20001;
double epsilon = std::numeric_limits::epsilon<double>();

double foo() {return global / 1000.0;}

int main(int,char**) {
  if (foo() - foo() > epsilon) {
    std::cout << "true\n";
  } else {
    std::cout << "false\n";
  }

  return 0;
}
Community
  • 1
  • 1
Jmc
  • 792
  • 1
  • 6
  • 16
  • Except that doubles _do_ evaluate to the same value every time. The problem is a side effect of Intel's extended precision in the registers. – James Kanze Jan 28 '14 at 19:18
  • Ah. I didn't know that. So it's an intel only problem? – Jmc Jan 28 '14 at 19:18
  • 1
    Today, probably. In the past, I think Motorola chips also used extended precision, and could thus display the same problem. – James Kanze Jan 28 '14 at 19:20
  • 1
    Also, if the compiler uses the SSE instructions for floating point, it shouldn't be a problem either. – James Kanze Jan 28 '14 at 19:23