Why is 0.1 + 0.05 not equal to 0.15? What solutions can be used in R?

Asked

Viewed 1,519 times

12

In the R:

0.1+0.05 == 0.15
[1] FALSE

Why does this happen? How to get around the situation (functions and packages to handle floating points)?

Editing:

This other question already contains general answers on floating point, thus restricting the present question to solutions in the R.

  • 1

    It’s the same if you use (0.1+0.05) == 0.15?

  • 1

    For the same reason described in that question

  • 5

    Dear friends, in order not to appear that the question is repeated, I will restrict the scope of the question to solutions only in the R. Note that the other question does not provide solutions to the R- packages, functions etc.

4 answers

12


I could not explain better than Guilherme Bernal why this happens. For this reason, I will limit myself to how to get around the situation within the R.

To understand how the R "see" 0.1, 0.05, 0.1 + 0.05 and 0.15, use command format

format(c(0.1, 0.05, 0.1 + 0.05, 0.15), digits = 20)
[1] "0.100000000000000005551" "0.050000000000000002776"
[3] "0.150000000000000022204" "0.149999999999999994449"

According to the R-Faq the way to make the comparison is through the command all.equal and not of the operator ==

all.equal(0.1 + 0.05, 0.15)
[1] TRUE

The documentation of all.equal recommends that, within a if, you use all.equal along with isTRUE, thus:

if (isTRUE(all.equal(0.1 + 0.05, 0.15))) {
    message("0.1 + 0.05 = 0.15")
}

10

Why the vast majority of programming languages represent floating points using the standard IEEE754 or something similar. The point is that the numbers are represented as follows:

(1.M) × 2E

Whereas M and E are integers written with a fixed number of houses. Thus:

0,0510 = 0.0000110011001100110011001100110011...   (vira uma dízima)
0,0510 = 1.1001100110011001 × 2(1012)
            M = 1001100110011001
            E = 0000000000000101

The big problem is that it is a tithe, some digits were inevitably lost in conversion. Converting this back to a base 10 you will have: 0.049999713897705078125. It’s very close to 0.05. If you do the same for 0.1 also you will find a result very close. Adding them you will have something close to 0.15. The problem when you test equality is that two different values but very close to 0.15 are still different.

So never test whether two floating points are equal directly. A good way to test is:

abs(a - b) < delta

Where delta is a small value, but not zero.

Other good alternatives (especially if you need exact values as in monetary calculations) is to use a fixed point number (i.e., represent as A/B, where A is a whole that you keep in memory and B is a fixed constant, for example 100) or represent with a pair of integers, forming rational numbers.

2

In a test performed with Java, the result when being of type double returns:

0.15000000000000002

But when we use the float, we have that 0.1F + 0.05F equals 0.15F:

System.out.println(0.1F + 0.05F == 0.15F);

Undoubtedly each OS and language has a particular treatment for floating point numbers, therefore there is no universal solution.

2

This is due to the conversion of real numbers into decimal to floating-point numbers on a binary basis (according to IEEE 754). In general, this happens when the number is not a sum of exponents of 2.

It should be understood that floats are represented in binary form, typically with 32 or 64 bits:

  • Signal bit (0 - positive, 1 - negative)
  • Exponent (8 or 11 bits)
  • Mantissa or fractional part (23 or 52 bits)

Where the formula is:

Base 2: (-1)sign 10exponent - (1111111 or 1111111111) 1 mantissa

Exceptions:

  • +0 and -0 have exponent = 0 and mantissa = 0

  • +Inf and -Inf have exponent = all 1s and mantissa = 0

  • Nans have exponent everything 1s and mantissa different from 0

These utilities ([1], [2]) help to understand the representation of 0.10 and 0.05 in single floats (32 bits). Experiment with 4.0, 2.0, (positive exponents of 2) 1.0 (exponent 0, coded as 127), 0.5, 0.25, 0.125, (negative exponents of 2), 0.375 (sum of 0.125 three times) and then with 0.1, 0.01, 0.3, etc..

For example, with the second utility, click on "Add An Analyzer". Select "decimal" on the left and enter 0.1. Select "Binary" on the right and enter 0.00011001100110011001101.

In this case, we observe a rounding:

0,110

= 0,000 1100 1100 1100 1100 1100 1100...2

0,000 1100 1100 1100 1100 11012 (rounding)

= Base 2: (-1)0 10111 1011 - 111 1111 1,100 1100 1100 1100 1101

The way to convert this number back to decimal basis is similar to integers, only each bit after the comma is multiplied with a negative exponent of 2:

Σi bi 2i, i PRA : min i max

In this case, min = -27, max = 0

0 20 + 0 2-1 + 0 2-2 + 0 2-3

+ 1 2-4 + 1 2-5 + 0 2-6 + 0 2-7

+ 1 2-8 + 1 2-9 + 0 2-10 + 0 2-11

+ 1 2-12 + 1 2-13 + 0 2-14 + 0 2-15

+ 1 2-16 + 1 2-17 + 0 2-18 + 0 2-19

+ 1 2-20 + 1 2-21 + 0 2-22 + 0 2-23

+ 1 2-24 + 1 2-25 + 0 2-26 + 1 2-27

= 0,100000001490116119384765625

0,1 (rounding off)

Even if there were no rounding in the input data, it does not mean that the results cannot be different than expected. For example, sums and subtractions suffer much more from the exponent difference than multiplications and divisions.

Often, what we see as an equality comparison (x = 2.5) in mathematical terms has to be an interval comparison in terms of floating comma (2.5 - ε x 2.5 + ε).

This ε (epsilon) should be a value in the scale of the value we are checking.

It doesn’t make sense that the difference is the smallest floating possible, because if you do, you’ll notice that 2.5 smallest floating possible ≈ 2,5. That is, the sum of fact is as if it did not happen, because the values are very far away on the scale. Precision is finite, as you would expect.

On the other hand, it’s also not very good to use the float immediately below or above (unless we want to even such accuracy), but perhaps the second, third, I don’t know, tenth float below or above.

In practical terms, it should be a function to do this work, if not a macro for when the comparison term is constant. This is because it is not common for programming languages to have syntax for binary floats, such as 10.12 (2,510), the more such a single-float 10,1000000000000001002 (8º single-floats above 2,5) or 10,01111111111110002 (8º single-float below 2.5). Or even if the language even has syntax, it is nothing readable or practical.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.