Is this a bug in max(x)?
Show older comments
I have the following two vectors:
a = [0.5 0.6 0.1 0.2 0.2]; b = [0.5 0.4 0.1 0.0 0.0];
and I now define x as their difference:
x = a - b,
which yields
x = [0 0.2 0 0.2 0.2].
You will notice there are 3 maximum values.
But, [~,I] = max(x) insists on calculating I = 4.
Why?
1 Comment
Guillaume
on 8 Jan 2018
Note that for vectors, I is always scalar (index of the occurence) regardless of how many times the maximum occur. If you want the index of all instances:
I = find(abs(x - max(x)) <= tol);
Answers (1)
"Is this a bug in max(x)?"
There is no error with MAX. Floating point numbers cannot exactly represent those values, and you forgot to take that into account in your calculation. Lets have a look at those values at a higher precision:
>> fprintf('%.30f\n',x)
0.000000000000000000000000000000
0.199999999999999955591079014994
0.000000000000000000000000000000
0.200000000000000011102230246252
0.200000000000000011102230246252
MAX is completely correct.
6 Comments
Ulrik William Nash
on 8 Jan 2018
Ulrik William Nash
on 8 Jan 2018
Edited: Ulrik William Nash
on 8 Jan 2018
"how do I make sure a difference of 0.2, for example, is 0.2 across the board, and not something different, albeit close?"
You can't. Basically for the same reason that you cannot write 1/3 precisely as a decimal fraction using a finite number of digits (try it and tell us how you get on), it is impossible to store 1/10 and 2/10 as binary floating point numbers with a finite number of bits. Any calculations using these values will accumulate this floating point error. Note also how your example shows that calculating 0.2 in two different ways can results in different error values.
Your task, as the programmer, is to write your code so that it never compares floating point numbers for equality, or assumes that there is no error while using floating point numbers. Here are some threads that might be useful for you:
and a thousand other threads where this has been discussed. You could have easily searched this forum yourself and found many answers telling what is happening. This is worth reading as well:
One simple solution for this particular example would be to multiply by ten and then round the values. This is not a general solution though!
max(round(10*x))
Guillaume
on 8 Jan 2018
"why are they different?" Because as Stephen said, your inputs cannot be stored exactly. See the output of
>> fprintf('%.30f\n', [a, b])
0.500000000000000000000000000000
0.599999999999999977795539507497
0.100000000000000005551115123126
0.200000000000000011102230246252
0.200000000000000011102230246252
0.500000000000000000000000000000
0.400000000000000022204460492503
0.100000000000000005551115123126
0.000000000000000000000000000000
0.000000000000000000000000000000
Only 0.5 and 0 are stored exactly. The others are stored by the nearest representable value in base 2. The delta to the nearest representable differs from number to number, so when you take the difference, the offset to 0.2 also differs.
"how do I make sure a difference of 0.2, for example, is 0.2 across the board, and not something different, albeit close?"
Round it to less significant digits. The number of significant digit should depend on the magnitude of your numbers.
x = round(a-b, 1)
Steven Lord
on 8 Jan 2018
Do this experiment for me, please. Find something on which to write and something with which to write. A piece of paper and a pencil, or a whiteboard and a marker, or a sandbox and a stick, would each work.
Using long division, compute x = 1/3 to as many decimal places as you want (though the only symbols you're allowed to use are the digits 0 through 9 and one decimal point. No "x = 0.3 repeating"; you have to write out as many decimal places as you want.) Now multiply 3*x.
In theory, 3*x should be exactly 1. In practice, because of round-off error in how you computed x, it will be slightly less than one third and so 3*x will be slightly less than 1.
In decimal, you can't exactly represent one third. x = 1/3 is "something different, albeit close". In decimal you can exactly represent two tenths as 0.2. In IEEE double precision, you can't represent either one third or two tenths exactly.
See this Answer and this Answer for more information. I particularly recommend the Cleve's Corner articles linked in each as well as Goldberg's paper "What Every Computer Scientist Should Know About Floating Point Arithmetic" linked in the second.
Using round might be useful in some specific cases, but note that it can introduce artifacts into the data that do not actually exist. Read these to know more:
Categories
Find more on Logical in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!