Clear Filters
Clear Filters

ismember returning false for 0.6000 == 0.6

85 views (last 30 days)
Jack
Jack on 1 Jul 2024 at 13:30
Commented: Matlab Pro on 8 Jul 2024 at 7:27
Hello,
I have a column of data that was created by using
A = 0.05:0.01:0.9
Secondly I am trying to obtain just the values of
B = [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
However when I run
[C idx] = ismember(B,A)
it returns the logical array
[1 1 1 1 1 0 1 1 1]
[6 16 26 36 46 0 66 76 86]
I have checked the workspace and confirmed that the value 0.6000 exists within A and even when I explicitly index it returns false
A(56)
returns
0.6000
and
A(56) == 0.6
returns logical 0.
Repeating this for the other values in B results in logical 1s as array C describes.
Thank you for any help you can provide!

Accepted Answer

Steven Lord
Steven Lord on 1 Jul 2024 at 13:48
This behavior is a consequence of floating point arithmetic. See this Answers post and the "Avoiding Common Problems with Floating-Point Arithmetic" section of this documentation page for more information.
If you are using the == operator to attempt to locate a floating-point number in an array, instead subtract the number you're trying to find from the numbers in the array and locate those positions where the difference is smaller than some tolerance or use the ismembertol function.
x = 0:0.1:1
x = 1x11
0 0.1000 0.2000 0.3000 0.4000 0.5000 0.6000 0.7000 0.8000 0.9000 1.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
As an example it appears that x contains the value 0.3, but it does not contain exactly 0.3.
checkWithExactEquality = x == 0.3
checkWithExactEquality = 1x11 logical array
0 0 0 0 0 0 0 0 0 0 0
It does contain a value that is extremely close to 0.3, however.
tolerance = 1e-15;
checkWithTolerance = abs(x-0.3) < tolerance
checkWithTolerance = 1x11 logical array
0 0 0 1 0 0 0 0 0 0 0
whichValueTolerance = x(checkWithTolerance)
whichValueTolerance = 0.3000
How far away from 0.3 is the value we found using a tolerance?
howDifferent = whichValueTolerance - 0.3
howDifferent = 5.5511e-17
To do the same with ismembertol:
checkWithIsmembertol = ismembertol(x, 0.3, tolerance)
checkWithIsmembertol = 1x11 logical array
0 0 0 1 0 0 0 0 0 0 0
whichValueIsmembertol = x(checkWithIsmembertol)
whichValueIsmembertol = 0.3000
The ismembertol function found the same value that the check with a tolerance did.
  3 Comments
Steven Lord
Steven Lord on 1 Jul 2024 at 14:29
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 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.
In decimal, you can exactly represent 0.6 with a finite number of decimal places but you can't exactly represent 1/3. In IEEE double precision, you can't exactly represent 0.6 (or nine-hundredths or one-tenth ...) with a finite number of bits.
Paul
Paul on 2 Jul 2024 at 4:17
"I would be less confused if C returned all zeroes."
That would depend on exactly how A is constructed using colon, :.
j = 0.05;
i = 0.01;
k = 0.9;
A = j:i:k;
According to the linked doc page:
x = j:i:k creates a regularly-spaced vector x using i as the increment between elements. The vector elements are roughly equal to [j,j+i,j+2*i,...,j+m*i] where m = fix((k-j)/i) (emphasis added)
One possibility is:
m = fix((k-j)/i);
A1 = A(1) + (0:m)*i;
[size(A), size(A1)]
ans = 1x4
1 86 1 86
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
isequal(A,A1) % nope
ans = logical
0
figure
plot(A-A1) % but close
Another might be
A2(1) = A(1);
for kk = 1:m
A2(kk+1) = A2(kk) + i;
end
[size(A) size(A2)]
ans = 1x4
1 86 1 86
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
isequal(A,A2) % nope
ans = logical
0
figure
plot(A-A2) % not close
So how does colon work?

Sign in to comment.

More Answers (2)

dpb
dpb on 1 Jul 2024 at 13:52
Edited: dpb on 1 Jul 2024 at 14:03
As the doc for ismember states (albeit only in the Tips section),
Tips
  • Use ismembertol to perform comparisons between floating-point numbers using a tolerance
Comparisons for floating point values are subject to the inevitable internal rounding of floating point representation by binary digits; there simply is no way to represent such values exactly and the rounding between the conversion of the ASCII representation of the value won't always (as you've discovered) be the same as that from a floating point conversion.
A = 0.05:0.01:0.9;
A(56)
ans = 0.6000
format long
A(56)
ans =
0.600000000000000
A(56)-0.6
ans =
1.110223024625157e-16
shows the actual difference is at the significance level of a double precision value; with the default format of the command window, the value was displayed as 0.6000 -- note particularly the trailing zeros that imply there's more that was rounded to the requested display precision.
See <Goldberg paper> with all the gory details of how floating point really works...

Matlab Pro
Matlab Pro on 1 Jul 2024 at 13:53
This is really annoying
I have once also witnesed this strange behaviour
Anyhow, this can be solved using a small "tolerance" method:
A = 0.05:0.01:0.9;
B = [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9];
[C idx] = ismembertol(B,A,eps)
C = 1x9 logical array
1 1 1 1 1 1 1 1 1
idx = 1x9
6 16 26 36 46 56 66 76 86
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
  5 Comments
John D'Errico
John D'Errico on 2 Jul 2024 at 0:47
I'd argue it is not even annoying. It just means you need to learn to deal with tolerances. Any choice of base will cause exactly the same problems. And if you insist on exact computations to avoid all such issues, your code will get exceedingly slow. That means if you want to do any serious computations, then you need to use some sort of floating point arithmetic. Doubles are the usual best compromise chosen, between number of bits and memory requirements. And doubles are the default choice made by most major computational environments.
Again, you might call it annoying. But is it really so? Suppose you moved to France. Would you claim it is annoying to need to learn at least a working knowledge of french? Similarly, if you would want to use MATLAB at all effectively, a working knowledge of mathematics, of your chosen field of interest, and surely of numerical analysis might all be appropriate.
Matlab Pro
Matlab Pro on 8 Jul 2024 at 7:27
Thx guys! It is always a good time to improve.. Now I am smarter :-).

Sign in to comment.

Products


Release

R2022b

Community Treasure Hunt

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

Start Hunting!