if and sum not working together

3 views (last 30 days)
Julian Holst
Julian Holst on 23 Sep 2022
Edited: James Tursa on 23 Sep 2022
I have come across a rather weird problem. When I calculate the sum of an array that I know is going to be e.g. 0.01, the if statement will not recognize it as such.
I am not that good in explaining, so here is a simple code:
a = zeros(10,1)+0.001;
b = sum(a)
if b ~= 0.01
c = 1
else
c = 0
end
Obviously the output should be: "b = 0.01 c = 0". But that's not what I'm getting. Instead I get a "c = 1".
Why does this happen?

Accepted Answer

Matt J
Matt J on 23 Sep 2022
Edited: Matt J on 23 Sep 2022
Because the sum cannot be done exactly in floating point. Therefore you need a tolerance,
a = zeros(10,1)+0.001;
b = sum(a)
b = 0.0100
if abs(b-0.01)>1e-10
c = 1
else
c = 0
end
c = 0

More Answers (2)

Steven Lord
Steven Lord on 23 Sep 2022
Please try this little experiment. Find something to write with and something to write on (ideally compatible things; pencil and paper not pencil and whiteboard.)
Step 1: Using long division (like you learned in school) divide 1 by 3. Call the result x. You are allowed to write as many decimal places of the result as you want, but only those you explicitly write can be used in step 2. No using 0.3 repeating to get "an infinite" number of places.
Step 2: Multiply x by 3. Call the result y.
In exact arithmetic we know (1/3)*3 is exactly 1. But the x value you defined in step 1 is not one third. It is slightly smaller than one third because you rounded off one third to fit it into x. If you've written one more decimal place in step 1 you'd have an x that's closer to one third than the x you actually used in step 2. Therefore y will not be 1. The value stored in y will be slightly smaller than 1.
Just as x in the example above is not exactly one third, 0.01 is not exactly one one-hundredth and 0.001 is not exactly one one-thousandth. We can use Symbolic Math Toolbox to convert them to numbers of the form N*2^e for integers N and e. The denominators are not exactly 100 or 1000 times the numerators respectively.
hundredth = sym(0.01, 'f')
hundredth = 
thousandth = sym(0.001, 'f')
thousandth = 

John D'Errico
John D'Errico on 23 Sep 2022
Edited: John D'Errico on 23 Sep 2022
I'll say the same thing, but differently.
As @Steven Lord points out, you cannot represent the fraction 1/3 exactly as a decimal. And while it seems like you could represent 0.01 exacly as a decimal, you have a problem. That is because computers generally do not use decimals internally. They use an IEEE binary form. Yes, they interface with you as if the numbers were in decimal. But are they that way internally? NO.
So if we try to represent the fraction 1/100 = 0.01, in binary, the result is an infinitely repeating BINARY number. It might look something like this:
0.0000001010001111010111000010100011110101110000101000111101 ...
Here I've used the ones to represent negative powers of 2. Personally, I call it a bicimal expansion.But those ones and zeros repeat infinitely.
And that means just because you write 0.01 in MATLAB, you don't get that number EXACTLY. In fact, you will get this:
sprintf('%0.55f',0.01)
ans = '0.0100000000000000002081668171172168513294309377670288086'
That is the closest number (as stored in BINARY) that MATLAB could find to approximate 0.01. It is within one bit in the least significant bit. But it is not truly exact. And that is because it cannot be exactly 0.01.
NEVER compare a floating point number to another floating point number. Well, at least unless you know enough about floating point numbers and how they are stored to know when you can do that with any degree of safety. And even then you probably should use a tolerance to compare floating point numbers for equality.
  1 Comment
James Tursa
James Tursa on 23 Sep 2022
Edited: James Tursa on 23 Sep 2022
I will differ slightly with this decimal expansion as being the closest decimal number to 0.01 that can be represented exactly in binary floating point (if that is what was meant):
sprintf('%0.55f',0.01)
ans = '0.0100000000000000002081668171172168513294309377670288086'
Exact binary-to-decimal conversion of binary floating point numbers (not BCD) with fractional parts always end with a decimal 5 digit. That's just the way it works out. The above ends with a 6 digit, so I know that it is not the exact conversion but a truncated/rounded conversion. You need to print more digits to see the exact conversion:
sprintf('%0.59f',0.01)
ans = '0.01000000000000000020816681711721685132943093776702880859375'
Smaller numbers can easily require hundreds of digits to get the "exact" binary-to-decimal conversion. E.g., here are some random numbers I picked off the top of my head with exact decimal conversions, always ending in a 5 digit:
sprintf('%0.95f',0.0001)
ans = '0.00010000000000000000479217360238592959831294137984514236450195312500000000000000000000000000000'
sprintf('%0.95f',0.1234567)
ans = '0.12345670000000000254836152180359931662678718566894531250000000000000000000000000000000000000000'
sprintf('%0.95f',0.3e-12)
ans = '0.00000000000029999999999999998386857470194773350090572741621919305998744675889611244201660156250'
sprintf('%0.230f',0.5e-50)
ans = '0.00000000000000000000000000000000000000000000000000500000000000000003808111852891171428799654582096356949475692364185476947407239503257194679766058733456227611266867455308990445808289826423731483373558148741722106933593750000000000'
I will also point out that the libraries used by sprintf( ) for PC versions of MATLAB only have the capability to print all these digits since R2017b. Using sprintf( ) for earlier PC versions won't give you these digits, but will give you a truncated/rounded result. You would have to use some other method to print them, such as the Symbolic Toolbox or my num2strexact( ) FEX function.

Sign in to comment.

Products


Release

R2022a

Community Treasure Hunt

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

Start Hunting!