4

I was solving an equation using double precision and I got -7.07649e-17 as a solution instead of 0.

I agree it's close enough that I can say it's equal but I've read that the machine epsilon for the C++ double type is 2^-52 which is larger than the value I get.

So why do I have an inferior value than the machine epsilon? Why isn't the value rounded to zero?

It's not a big deal but when I do a logical test it appears that my value is not zero...

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Random Tourist
  • 151
  • 1
  • 1
  • 12
  • Why wouldn't it be able to contain that value? – user253751 Apr 28 '17 at 11:22
  • @immibis Yes I should have asked myself this question first... I thought that the machine epsilon was the smallest computable number but Dialecticus showed me it's not. And I should have read that as well: https://en.wikipedia.org/wiki/Double-precision_floating-point_format ... – Random Tourist Apr 28 '17 at 13:43

3 Answers3

6

There are two different constants in this story. One is epsilon, which is a minimal value that when added to 1.0 produces a value different from 1.0. If you add a smaller value to 1.0 you will again get a 1.0, because there are physical limits to the representation of a number in a computer. But there are values that are less than epsilon and greater than zero. Smallest such number for a double you get with std::numeric_limits<double>::min.

For reference, you get epsilon with std::numeric_limits<double>::epsilon.

Dialecticus
  • 16,400
  • 7
  • 43
  • 103
  • Oh, so there are values acting as `0` when summed with `1.0` but without being equal to `0`, weird... But thanks! Do you know what is the point of allowing those values? – Random Tourist Apr 28 '17 at 13:28
  • 1
    @RandomTourist lets say that for some float type, `epsilon` is 0.01. Then `1.0 + 0.005 == 1.0`, but `0.1 + 0.005 != 0.1` – Caleth Apr 28 '17 at 13:34
  • 1
    @Caleth Ah! Are you implying that `0.1 + 0.005` can be seen as `(1.0 + 0.05) / 10` and because `0.05 > epsilon` it can be computed without loss of precision? It does make sense thanks! – Random Tourist Apr 28 '17 at 13:40
  • @RandomTourist Yes, the epsilon isn't an absolute value, but a ratio. `1.0E20 + 1.0 == 1.0E20` also holds – Caleth Apr 28 '17 at 14:31
  • @Caleth Yes I understand better now, basically `x + y != x` if `y/x >= epsilon`. Thanks! – Random Tourist Apr 28 '17 at 15:56
  • 1
    @RandomTourist You can compare binary floating-point formats with decimal ones; if I store a number in decimal with 2 significant figures, then I can write down 1.0, and 1.0 + 0.10 = 1.1, but 1.0+0.099 = 1.0 (if rounding down). But 0.099 is a perfectly valid number with 2 significant figures and I can still do calculations like 0.099+0.0010 = 0.10 – user253751 Apr 28 '17 at 21:41
0

You are not guaranteed that rounding will take place at any particular time. The C++ standard permits the implementation to use additional precision pretty much anywhere it wants to and many real-world implementations do exactly that.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 1
    I'd appreciate an explanation from the downvoter. If this is incorrect, I'd like to correct it. If it's unclear, I'd like to clarify it. – David Schwartz Apr 28 '17 at 11:13
  • Explanation is simple; this doesn't answer the question. – Lightness Races in Orbit Apr 28 '17 at 11:51
  • Thanks for this answer but why using additional precision? I thought that the type already set the precision... – Random Tourist Apr 28 '17 at 15:32
  • @BoundaryImposition The question asks why it isn't rounded to zero. That it's not required to be rounded to zero, IMO, answers the question. – David Schwartz Apr 28 '17 at 18:45
  • 1
    @RandomTourist Imagine a machine that only had floating point registers that store doubles. So you can load a single precision value into the register or round to single precision when you store, but not do operations directly on single precision. The language doesn't require such a platform to round intermediate values and permits the implementation to use a higher precision representation wherever that's convenient for the implementation. Your platform is like this. – David Schwartz Apr 28 '17 at 18:47
  • I disagree. The question asks why it isn't rounded to zero. It does not ask whether this behaviour is compliant to ISO C++. ("Because it doesn't have to be" isn't really a useful answer!) – Lightness Races in Orbit Apr 28 '17 at 18:47
  • @DavidSchwartz: _"Since it's not required, it isn't"_ Right, therefore we can already deduce that fact for ourselves, and this answer adds nothing over what was already observed by the OP before posting. It reminds me of my favourite line from last week's Doctor Who: _"Why is she here?"_ _"Because she's not anywhere else."_ – Lightness Races in Orbit Apr 28 '17 at 18:48
  • Looks like we're going to have to agree to disagree, as I don't have time to be drawn into a big debate over the issue. Suffice it to say I stand by the above and another answer has actually explained what is happening to the OP's variable. – Lightness Races in Orbit Apr 28 '17 at 18:50
  • @DavidSchwartz: Exactly. Glad we agree! – Lightness Races in Orbit Apr 28 '17 at 23:47
  • @BoundaryImposition The question asks why it *can* happen, not what it *does* happen. The reason it *can* happen is that the standard permits the implementation to use additional precision. It really is that simple. This is a "why is this allowed" question, not a "how is this implemented" question. – David Schwartz May 02 '17 at 19:14
  • Not really sure we need to get into this _again_ David. Can we move on please? – Lightness Races in Orbit May 02 '17 at 19:16
  • Just replacing a large number of comments with a single summary comment. (Notice that I removed the earlier comments.) Sorry, I didn't mean to tag you in it. – David Schwartz May 02 '17 at 19:22
-4

A common solution for the floating point precision problem is to define an epsilon value yourself and compare to that instead of zero.

e.g.

double epsilon = 0.00001;
if (abs(value) < epsilon) // treat value as 0 in your code
xander
  • 1,780
  • 9
  • 16