Why is 0.3 - 0.1 - 0.2 ~= -0.1 - 0.2 + 0.3

18 visualizzazioni (ultimi 30 giorni)
John D'Errico
John D'Errico il 27 Dic 2020
Modificato: John D'Errico il 27 Dic 2020
I see this same question over and over again on Answers. Why do computations like this fail in MATLAB?
0.3 - 0.1 - 0.2 == -0.1 - 0.2 + 0.3
ans = logical
0
Then the question becomes, is this a bug in MATLAB?
Of course not. And we can find similar issues in any language that employs floating point arithmetic. In fact, we can even find a FAQ entry on the question. I'll try to offer in my answer an explanation of what happens, down at the level of the least significant bits of the numbers.

Risposta accettata

John D'Errico
John D'Errico il 27 Dic 2020
Modificato: John D'Errico il 27 Dic 2020
The answer arises by understanding how floating point numbers are stored in MATLAB.
Here are 0.3, 0.1, 0.2, expressed each as what I might call binary fractional numbers. I've written the bit sequences for the numbers as strings of 0 and 1 bits.
0.3: '0.010011001100110011001100110011001100110011001100110011'
0.1: '0.00011001100110011001100110011001100110011001100110011010'
0.2: '0.0011001100110011001100110011001100110011001100110011010'
In all cases, these numbers are truly repeating binary sequences. The binary form for these fractions extends to infinitely many bits, just as we cannot represent the fraction 2/3 exactly as a decimal number. But remember that a double can store only 52 bits of information.
(Those results were produced by a little toy I wrote some years ago, num2bin. I've attached num2bin to this answer for those who are interested.)
num2bin(0.3)
ans =
struct with fields:
Class: 'double'
Sign: 1
Exponent: -2
Mantissa: '10011001100110011001100110011001100110011001100110011'
BinaryExpansion: [1×27 double]
BiSci: '1.0011001100110011001100110011001100110011001100110011 B-2'
BiCimal: '0.010011001100110011001100110011001100110011001100110011'
We can write any floating point number in MATLAB as a sum of positive and negative powers of 2. Thus here, we see
BE = num2bin(0.3).BinaryExpansion
BE =
Columns 1 through 21
-2 -5 -6 -9 -10 -13 -14 -17 -18 -21 -22 -25 -26 -29 -30 -33 -34 -37 -38 -41 -42
Columns 22 through 27
-45 -46 -49 -50 -53 -54
Those are the powers of 2 that we use to represent the decimal fraction 3/10 = 0.3. We can even reconstruct the original number from those powers of 2.
sum(2.^BE)
ans =
0.3
sum(2.^BE) == 0.3
ans =
logical
1
I'll use num2bin to see what happens in depth to the numbers in these similar computations? Why do they produce different results?
[0.3 - 0.1 - 0.2, -0.1 - 0.2 + 0.3]
ans =
-2.77555756156289e-17 -5.55111512312578e-17
Look carefully at what happens. In the former case, we can write it as
(0.3 - 0.1) - 0.2
So we first compute 0.3 - 0.1. That result is stored temporarily, as a
double in some temporary register by MATLAB. And THEN we subtract 0.2. The intermediate result, 0.3 - 0.1 is stored internally as the binary number:
'0.0011001100110011001100110011001100110011001100110011001'
Compare that result to 0.2 alone, which is stored as
'0.0011001100110011001100110011001100110011001100110011010'
Can you see the bit sequences are subtly different at the end, in the least significant bits? The difference arises because we can store only 52 binary bits in the result. When we now subtract 0.2 from the intermediate result, thus forming
(0.3 - 0.1) - 0.2
the result ends up as effectively
2^-55 - 2^-54 = -2^-55
And what is 2^-55? (2^-55 is actually stored properly as a double, since it is an exact power of 2.)
-2^-55
ans =
-2.77555756156289e-17
0.3 - 0.1 - 0.2
ans =
-2.77555756156289e-17
Of course we know that real mathematics would produce a true zero, but numbers stored in floating point form are often not the numbers we think are stored. They are only approximations thereof.
What changes when we change the sequence of the elements in that computation? Now consider the expression:
-0.1 -0.2 + 0.3
ans =
-5.55111512312578e-17
Again, the result should mathematically be zero, but just as alarmingly to new users, the result is different from the previous one. Again, we can break this down into two subproblems. We perform the first computation as (-0.1 - 0.2), and THEN we will add 0.3.
-0.1 -0.2 + 0.3
ans =
-5.55111512312578e-17
num2bin(-0.1 - 0.2 + 0.3).BinaryExpansion
ans =
-54
-2^-54
ans =
-5.55111512312578e-17
So we see the two computations, performed in a subtly different sequence, result in different answers. It is not always true that we have a difference. For example, we find that
(0.2 + 0.3 - 0.4) == (-0.4 + 0.2 + 0.3)
ans =
logical
1
and
-4/7 + 1/7 + 3/7 == 1/7 - 4/7 + 3/7
ans =
logical
1
So two results that did produce the expected answer. And then, we find this:
12/13 - 3/13 - 9/13 == 12/13 - 9/13 - 3/13
ans =
logical
0
The point is to never trust the least significant bits of a computation. At least, not until you understand those computations well enough to know when not to trust them. And even then? Be careful, be very careful.

Più risposte (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by