4

I just wrote the following code in C++:

double variable1;
double variable2;
variable1=numeric_limits<double>::max()-50;
variable2=variable1;
variable1=variable1+5;
cout<<"\nVariable1==Variable2 ? "<<(variable1==variable2);

The answer to the cout statement comes out 1, even when variable2 and variable1 are not equal.Can someone help me with this? Why is this happening?

I knew the concept of imprecise floating point math but didn't think this would happen with comparing two doubles directly. Also I am getting the same resuklt when I replace variable1 with:

double variable1=(numeric_limits<double>::max()-10000000000000);

The comparison still shows them as equal. How much would I have to subtract to see them start differing?

gandalf34
  • 49
  • 3
  • 6
    This question has been asked many times before. Try searching for floating point precision. The short answer is that floating point numbers have a limited number of significant digits. -50 and +5 are too small to affect the value of numeric_limits::max(). – Peter Ruderman Aug 04 '11 at 12:54
  • If a star is 100 light years away and it were to move 1 m further from us, would you want me to say it is now 100 light years and 1 m from us? – UncleBens Aug 04 '11 at 15:42
  • @UncleBens: Why not? Maybe the star is a black hole, and moving it a meter away might give such better light bending that it uncovers some previously invisible galaxies behind it. But `float` and `double` might not be appropriate storage for that. You don't want to suggest that it is an explicitly designed feature of floating-math that it hides the non-interesting stuff, instead of it being just a compromise? – Sebastian Mach Aug 04 '11 at 15:57

6 Answers6

11

The maximum value for a double is 1.7976931348623157E+308. Due to lack of precision, adding and removing small values such as 50 and 5 does not actually changes the values of the variable. Thus they stay the same.

slaphappy
  • 6,894
  • 3
  • 34
  • 59
  • Does this mean if I keep repeatedly subtracting 50 from a double with max value, I will not observe any change because the variable remains the same? – gandalf34 Aug 04 '11 at 13:08
  • 1
    Yes. If you want *any* change to your variable if its value is `max()`, you must add approximately `10^280`. (warning: super wild estimate ;) – slaphappy Aug 04 '11 at 13:11
  • So, my double variable will cause an infinite loop if I keep subtracting 50 from it repeatedly until it goes zero? Because it will never go zero if it is at max value? – gandalf34 Aug 04 '11 at 13:14
  • Yes. If you subtract a small enough value from a float/double, this has the same effect as subtracting zero. It's generally quite dangerous to iterate upon float arithmetic. – slaphappy Aug 04 '11 at 13:19
  • So that would mean that writing loops that depend on high valued doubles for exit is not possible? Something like, x=max; while(x>minimum_of_x){x=x-50;} – gandalf34 Aug 04 '11 at 13:26
  • It's possible, but for values of x > some_number, the loop will never exit. You should work on integers instead. – slaphappy Aug 04 '11 at 13:41
  • Is there any way to find out that for a given value of x, what is the critical value of some_number beyond which the loop will be stuck? Many thanks for your input :) – gandalf34 Aug 04 '11 at 13:44
  • Wikipedia says double have 15.95 decimals digits of precision. So if the value you subtract is d, the max you can allow for x is 10^15 times the smallest power of 10 of d, i.e. d = 120, so x is at most 10^15 * 10. However, I would advise using 10^14 to be sure. – slaphappy Aug 04 '11 at 13:53
  • 1
    @gandalf34: It's platform dependent, but most platforms use IEEE floating point numbers, which have 51 bit mantissa, which gives you about 15 significant decimal digits. If the exponents differ by more than that, adding the numbers will have no effect. – Jan Hudec Aug 04 '11 at 14:04
3

There isn't enough precision in a double to differentiate between M and M-45 where M is the largest value that can be represented by a double.

Imagine you're counting atoms to the nearest million. "123,456 million atoms" plus 1 atom is still "123,456 million atoms" because there's no space in the "millions" counting system for the 1 extra atom to make any difference.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
2

numeric_limits<double>::max()

is a huuuuuge number. But the greater the absolute value of a double, the smaller is its precision. Apparently in this case max-50and max-5 are indistinguishable from double's point of view.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
0

You should read the floating point comparison guide. In short, here are some examples:

float a = 0.15 + 0.15
float b = 0.1 + 0.2
if(a == b) // can be false!
if(a >= b) // can also be false!

The comparison with an epsilon value is what most people do.

#define EPSILON 0.00000001

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

In your case, that max value is REALLY big. Adding or subtracting 50 does nothing. Thus they look the same because of the size of the number. See @RichieHindle's answer.

Here are some additional resources for research.

Community
  • 1
  • 1
  • 1
    -1: This is not about fuzzy comparison, but about adding numbers of vastly different orders of magnitude, which is totally unrelated issue. – Jan Hudec Aug 04 '11 at 13:10
0

From the C++03 standard:

3.9.1/ [...] The value representation of floating-point types is implementation-defined

and

5/ [...] If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression is a constant expression (5.19), in which case the program is ill-formed.

and

18.2.1.2.4/ (about numeric_limits<T>::max()) Maximum finite value.

This implies that once you add something to std::numeric_limits<T>::max(), the behavior of the program is implementation defined if T is floating point, perfectly defined if T is an unsigned type, and undefined otherwise.

If you happen to have std::numeric_limits<T>::is_iec559 == true, in this case the behavior is defined by IEEE 754. I don't have it handy, so I cannot tell whether variable1 is finite or infinite in this case. It seems (according to some lecture notes on IEEE 754 on the internet) that it depends on the rounding mode..

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
-1

Please read What Every Computer Scientist Should Know About Floating-Point Arithmetic.

otto
  • 1,138
  • 6
  • 14
  • 3
    `-0.5` for posting a link to very very long article which doesn't directly answer the question (which is terrifying to newbies); `-0.5` for not saying anything yourself, in brief.`total = -1`. This link is better suited for comments. – Nawaz Aug 04 '11 at 12:57
  • @Nawaz: I disagree. The link does answer the question and gives most detailed explanation. So I think it's appropriate as an answer here. – Jan Hudec Aug 05 '11 at 05:46
  • @Jan: No. What is the difference between this link and giving a link to the C++ Standard (working drafts), pertaining to C++ questions, and thinking that it answers the question? The point is, the OP has to read a lot just to get his answer. The link explains a hell lot of thing which might scares anyone if he wants to know answer in brief. You can give the link, but at the same time, also explain it briefly; once you explained it, then provide the link, saying `"if you want to know this in detail, then follow this link".` – Nawaz Aug 05 '11 at 05:52
  • @Nawaz Okay. I could've at least quoted the most relevant part of the article and explain why it's relevant. I think my attempt at answering the question ended up being a bit condescending. – otto Aug 05 '11 at 06:49