Doubles that are *exactly* equal are returning 0 for (a==b)

Go To StackoverFlow.com

2

Possible Duplicate:
C compiler bug (floating point arithmetic)?

I've got two doubles which I can guarantee are exactly equal to 150 decimal places - ie. the following code:

printf("***current line time is %5.150lf\n", current_line->time);
printf("***time for comparison is %5.150lf\n", (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS));

...returns:

***current line time is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
***time for comparison is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

FIVE_MINUTES_IN_DAYS is #defined, and current_line->time and last_stage_four_print_time are both doubles.

My problem is that the next line of my debugging code:

printf("if condition is %d\n", (current_line->time >= (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS)));

returns the following:

if condition is 0

Can anyone tell me what's going on here? I am aware of the non-decimal/inexact nature of floats and doubles but these are not subject to any error at all (the original figures have all been read with sscanf or #defined and are all specified to 10 decimal places).

EDIT: My mistake was assuming that printf-ing the doubles accurately represented them in memory, which was wrong because one value is being calculated on-the-fly. Declaring (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS) as threshold_time and using that instead fixed the problem. I will make sure to use an epsilon for my comparisons - I knew that was the way to go, I was just confused as to why these values which I (incorrectly) thought looked the same were apparently inequal.

2012-04-04 06:52
by Matt Lyons
What is the #define for FIVE_MINUTES_IN_DAYS? Is it a long double? There's a lot you're not showing us - Potatoswatter 2012-04-04 07:01
Wow, 7 replies in like 5 minutes :D FIVEMINUTESIN_DAYS is defined as #define FIVE_MINUTES_IN_DAYS 0.0034722222Matt Lyons 2012-04-04 07:03
Store your a+b into a float. Use a unsigned char pointer and cast the address of the float into it and check whether the bit patterns of the two floats really match. If they do, then there's something fishy. Else the answers given here are the ways to g - Pavan Manjunath 2012-04-04 07:08
@MattLyons: Yeah, looks like people are passionate about floating point math :-) I think a lot of us have been bitten - Eric J. 2012-04-04 07:17
@MattLyons A few comments. The definition of FIVEMINUTESIN_DAYS you provided above is only accurate to single precision while your variables are doubles. Defining the value as (5./(24.*60.)) lets the compiler calculate the value to double accuracy. Also, to display the exact internal representation of a double, use a %a format. Finally, since it hasn't been mentioned, a reason calculated and stored doubles can differ on intel architecture is 80-bit registers being used for the calculations before results are stored to 64-bit doubles in memory - Brian Swift 2012-04-04 09:35


4

Floats certainly are not accurate to 150 significant digits, so I 'm not sure what conclusion can be drawn from the "visual" comparison (if any).

On the other hand, the values are obviously not bit-identical (and how could they be, since one of them is calculated on the spot with addition?). So it's not really clear why the behavior you see is unexpected.

Don't ever compare floats like that, just do the standard comparison of difference vs epsilon.

2012-04-04 06:58
by Jon
I know the 150 digit bit was inaccurate, I was using that to check the values in memory. You're exactly right about calculation on the spot - declaring a variable threshold_value as (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS) fixed the problem - Matt Lyons 2012-04-04 07:06


4

Read about floating point representation (particularly http://en.wikipedia.org/wiki/IEEE_754-2008). Try printing the actual contents of the bytes containing the doubles as hexadecimal and they won't match bit for bit. The proper comparison for floats is in Knuth (Seminumerical algorithms). Simply (replace bool with int and float with double, true with 1):

bool almostEqual(float x, float y, float epsilon)
{
    if (x == 0.0 && y == 0.0) {
        return true;
    }

    if (fabs(x) > fabs(y)) {
        return fabs((x - y) / x) < epsilon;
    } else {
        return fabs((x - y) / y) < epsilon;
    }
}
2012-04-04 07:00
by subhacom
The way to print the byte contents: void main() { double a = 3.0; char * b = (char*)&a; int ii = 0; for (ii = 0; ii < sizeof(a); ++ii){ printf("%x ", *(b+ii)); } - subhacom 2012-04-04 07:08
+1 and the rule is: "Never, ever compare floats directly, unless it's a zero" ;- - Arsen7 2012-04-04 07:13
Please add those two lines as first check in your method to make your code NaN aware: if (x!=x) { return (y!=y)? true : false; } if (y!=y) {return false; - Totonga 2012-04-04 07:28
@Totonga Good point, I'd rather put it like: if (x!=x && y!=y) return false; [because x == x is false if x is NaN, in the same vein almostEqual(x, x) should return false for NaN] ;- - subhacom 2012-04-04 09:49
@subhacom I assume x == x is also true for NaN because everything is true for NaN. So my suggestion was if x = NaN and y = NaN the method should return true because both are equal. If one of them is NaN you hae to return false - Totonga 2012-04-04 11:31


3

You should always use an EPSILON value for comparison of floats and doubles to check for equality. Even though it looks the same the internal representation is not guaranteed to match because of the way these types of numbers are represented in binary.

You can do something like

#define EPSILON 0.00001
...
if (fabs(a - b) <= EPSILON) return 1; // they are equal
return 0;
2012-04-04 06:56
by Jesus Ramos


2

Jesus is right about how to solve this.

As for why... in one case you read in a constant value, in the other case you perform an addition operation. Even if the printed output is exactly the same, the binary representation can be slightly different.

Try inspecting the memory backing the two double's and see if any bits are different (there will be differences).

For a comprehensive treatment, I recommend

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

2012-04-04 06:59
by Eric J.
As for why... in one case you read in a constant value, in the other case you perform an addition operation. - nailed it, see the edit : - Matt Lyons 2012-04-04 07:15


1

In general you shouldn't use == to compare floats or doubles. You should instead check that the difference is smaller than some small number.

double second_number = last_stage_four_print_time + FIVE_MINUTES_IN_DAYS;
if (fabs(current_line->time - second_number) < 0.001 || current_line->time > second_number){
  // your comparison code
}
2012-04-04 06:59
by Waynn Lue


1

First, doubles have just 15-16 decimal places (log_2 of 52 bit matissa).

Second, if you want to compare, use the already mentioned epsilon.

Thirdly, for debugging, print the hex value.

2012-04-04 07:00
by flolo
Ads