Is this a bug in max(x)?

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

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);

Sign in to comment.

Answers (1)

Stephen23
Stephen23 on 8 Jan 2018
Edited: Stephen23 on 8 Jan 2018
"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

My first question is why are they different? But more importantly for now, how do I make sure a difference of 0.2, for example, is 0.2 across the board, and not something different, albeit close?`
Ulrik William Nash
Ulrik William Nash on 8 Jan 2018
Edited: Ulrik William Nash on 8 Jan 2018
Okay, I guess one way is to use round(x,p) before looking for the index, where p is the precision needed.
Stephen23
Stephen23 on 8 Jan 2018
Edited: Stephen23 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))
"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)
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.

Sign in to comment.

Tags

Asked:

on 8 Jan 2018

Edited:

on 8 Jan 2018

Community Treasure Hunt

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

Start Hunting!