-2

I use relative comparison as it described here. I have two doubles. The first of them is result of computation:

double d1(callComputation() );

The second is defined as following:

double d2(0.009);

My realization of approximatelyEqual returns false and it is right because:

      std::printf("d1 = %25.20f\n", d1);

prints:

d1 =    0.00900000000000011902

and

      std::printf("d2 = %25.20f\n", d2);

prints:

d2 =    0.00899999999999999932

I expected the d2 will be represented approximately as follows:

0.00900000000000011239 or 0.00900000000000000000 etc 

and the result of comparison of d1 and d2 will be true. But even approximatelyEqual doesn't work proper. How should I compare in such cases? I don't need high precision of computations but obviously I'm interested in the stability of computation results. Maybe fixed amount of digits after point (for instance two) will help to fix this problem?

mhd
  • 535
  • 1
  • 5
  • 12
  • This is actually really very hard, and probably unanswerable unless you tell us the exact calling context. As a starting point, along with the question you linked, read https://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf. – Bathsheba Aug 11 '18 at 16:44
  • As the author of that post said, the result depends on `epsilon`. – meowgoesthedog Aug 11 '18 at 16:48
  • You didn't give your epsilon, are we deemed to guess ? –  Aug 11 '18 at 19:24
  • I used the machine epsilon: std::numeric_limits::epsilon() I'm newbie in this problem and I thought the machine epsilon was an universal choice. Now I think about how to choose the epsilon for given precision because there is no universal epsilon. As far as I understand I need a function to calculate needed epsilon for double like that: double epsilon (int numOfDigitsAfterFloatPoit); – mhd Aug 12 '18 at 20:41
  • @mhd Contrary to what the name implies, `std::numeric_limits::epsilon()` is almost never a good value to use as “epsilon” for either absolute or relative comparison as in the answer you linked to. Ideally, you would know the value. If you have absolutely no idea but must use something, a reasonable candidate for relative comparison as implemented in the answer you link to is the square root of `std::numeric_limits::epsilon()`, or `1.0e-8` for short. – Pascal Cuoq Aug 23 '18 at 17:55

1 Answers1

1

First, that is the closest value to .0009 that can be represented as a double precision IEEE floating point number.

Every floating point operation can result in a rounding operation. Epsilon based strategies are not reliable to determine if two floating point calculations represent the "same" real number.

Determining if two calculations result in, or do not result in, the same real number in the general case is actually incomputable; you can reduce Halt to it.

The "best" most expensive general purpose way to determine if they could be equal is interval mathematics. Replace your doubles with a pair of doubles representing an interval. In each calculation ensure that the result of the upper double is rounded up, and the lower double is rounded down (and when dividing/subtracting, ensure you don't flip the interval, as division/subtraction is monotonically decreasing; same for multiplying by negatives). If you divide an interval containing 0 by an interval containing 0, ensure both values become NaN, and if dividing by an interval containing 0 the result is +inf to -inf.

There are a few problems with interval mathematics; it is expensive (significantly more than 2x), requires replacing every primitive operation with ones that control rounding up/down, and you will find that the interval ends up being shockingly large after a non-trivial amount of math.

Traditional IEEE rounding produces values that are usually closer to what you want because it in effect randomly rounds each iteration. If you flip a coin 10,000 times, you'll get a random value with mean 5000 and variance 2500 and standard deviation 50; so 97% of the time you'll be in 4900 to 5100, 99%+ in 4850 to 5150. Each rounding operation is in effect a coin adding + or - eplsion/2 (actually better; it adds a value between +/- epsilon/2 chosen as the smallest one), so after 10k ops the likely error is less than +/- 75*epsilon. Interval math, meanwhile, will state that the certain error is +/-10000 epsilon.

TL;DR

You need a bigger epsilon.

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • How to choose the value of an epsilon depending on the number of digits after the decimal point? For instance for 1, 2, 3 digits after the point? – mhd Aug 11 '18 at 17:27
  • 1
    @mhd You need to choose the epsilon relative to the magnitude of the numbers - contrived example two numbers: 1.00000000e20 and 1.00000001e20 difference is 1.0e12 ie still a big number. – Richard Critten Aug 11 '18 at 17:37