Search code examples
rfloating-accuracy

Why isn't 90 - .Machine$double.eps less than 90?


I must be missing something with my understanding of precision here, but I thought that R could represent numbers along a grid with step size .Machine$double.eps, but this appears not to be the case; in fact:

90 - .Machine$double.eps == 90
# [1] TRUE

This is strange to me because these two numbers (1) can be represented and (2) are non-zero:

sprintf('%.16a', c(90, .Machine$double.eps))
# [1] "0x1.6800000000000000p+6"  "0x1.0000000000000000p-52"

The first place where the difference is numerically non-zero is even more suggestive:

90 - 32*.Machine$double.eps < 90
# [1] FALSE
90 - 33*.Machine$double.eps < 90
# [1] TRUE

This kind of result points straight to precision issues but there's some gap in my understanding here...

If 90 - .Machine$double.eps == 90, why isn't double.eps larger on my machine?

The results here suggest to me that actually I should have .Machine$double.eps == 2^5 * .Machine$double.eps...


Solution

  • The effect is known as loss of significance (https://en.wikipedia.org/wiki/Loss_of_significance). The significant digits of 90 shift the .Machine$double.eps away. Try

    (90 - 46*.Machine$double.eps) == 90
    

    this should give you FALSE.
    Definition of a machine.eps: it is the lowest value eps for which 1+eps is not 1

    As a rule of thumb (assuming a floating point representation with base 2):
    This eps makes the difference for the range 1 .. 2,
    for the range 2 .. 4 the precision is 2*eps
    and so on.

    x <- 3.8
    (x + 2*.Machine$double.eps) == x
    x <- 4
    (x + 2*.Machine$double.eps) == x
    # ...
    x <- 63
    (x + 32*.Machine$double.eps) == x
    x <- 64
    (x + 32*.Machine$double.eps) == x
    

    The absolute precision of the floating point representation varies with x, but the relative precision is nearly constant over the range of the floating point numbers.