- Undocumented Matlab - https://undocumentedmatlab.com -
Matlab numerical gotchas
Posted By Yair Altman On October 30, 2013 | 9 Comments
In deference to Halloween, I thought of posting a short note on a couple of Matlab numerical gotchas. They are not really undocumented – in some cases they are specifically documented and in other cases can be inferred from the documentation. What they share in common is that for some reason they are not widely known. Their importance comes from the fact that I often encounter them in Matlab code, and they often do not immediately cause syntax error, but rather more difficult-to-diagnose incorrect results. So I thought it would be worthwhile to formally document them here:
[1]
This one came to me from Baruch Karlin:
In a program using math functions, it is easy to forget to limit the values of interim computations to the boundaries expected in the physical world. One specific example is that while we normally think of inverse trig functions (e.g., asin and acos) as returning the angles corresponding to the specified trig function result, in fact such inverse-trig functions are defined mathematically [6] such that their range extends past the “standard” domain (-1 to +1 in the case of asin and acos):
asin(10)
is just as valid in Matlab as asin(1)
. The results of such “invalid” values are standard complex values, which Matlab treats as regular numeric values. This is often an undesirable effect that would cause incorrect results downstream in the program code:
>> value = some_computation();
>> result = asin(value) %value=10 in this example
result =
1.5707963267949 - 2.99322284612638i
As the snippet shows, it is sometimes difficult to immediately see that the value
variable could receive an inappropriate value, resulting in a complex result
. Assuming this is undesirable, we could clamp the value:
value = some_computation();
value = min(max(real(value),-1),1); %value is clamped to [-1,+1]
result = asin(value); % result is assured to be real
This is an oldie, in this case well predating Matlab, going back all the way to the early days of floating-point arithmetic. It is probably the most widely-known and yet most widely-encountered gotcha in any programming language since the days of yore. It is featured in the semi-official Matlab FAQ [7], which explains the reason that:
>> 1 == (3 -1.1 -0.9)
ans =
0 %=false!
>> 2 -1.1 -0.9
ans =
-1.11022302462516e-016
The workaround is to use eps whenever comparison of non-integer numeric values is needed, and indirect comparison (e.g., < or >) rather than direct ones (== or ~=).
The reader in referred to the above-mentioned FAQ, or to one of the following resources:
This one is also an oldie from the semi-official Matlab FAQ [7] (a bit paraphrased):
Since 81/3=2, we naturally expect to find that (-8)1/3=-2. Unfortunately we find in Matlab that:
>> (-8)^(1/3)
ans =
1 + 1.73205080756888i
In the same way there are two solutions (plus and minus) for the square root of a positive real number, there are multiple solutions for roots of negative (and complex) numbers. If you express a number as A*eiθ
, its cube root takes the form (A1/3)*ei(θ+2kπ)/3, for k=0:2. Since -8 is 8eiπ, θ=π or -π. Therefore, the cube roots of -8 are 2eiπ/3, 2e-iπ/3, and 2eiπ. The last one simplifies to the expected real value of -2.
So we see that the cube root (and in general, the n-th root) always returns 3 roots (or in general, n roots), and Matlab chooses one of them, which may not necessarily be non-complex. As in the first gotcha, the resulting complex value is correct in the mathematical sense, but could well lead to erroneous results downstream in the Matlab code.
Matlab always returns the first solution counter-clockwise from the positive real axis. Armed with this knowledge, we can compute all or some particular root. For instance, if you want the negative real cube root, simply take the cube root of the absolute value of the number, and negate it. For a different wording and more information, see MathWorks Tech Solution 1-15M1N [14].
Joe Sababa suggested a method to find all the roots at once using roots on the polynomial, effectively solving x3+8=0
for x:
>> P=[1 0 0 8]; roots(P) % solve x^3 + 8 = 0
ans =
-2
0.999999999999999 + 1.73205080756888i
0.999999999999999 - 1.73205080756888i
(note the FP inaccuracy, discussed in the previous gotcha)
An alternate method to obtain -2 as a cube root of -8 is to use the nthroot function:
>> x = nthroot(-8, 3)
x =
-2
Usage of “+” and “-” operators is normally intuitive. Matlab knows when we refer to the unary operation and when to the binary one. But it gets somewhat less intuitive in numeric arrays:
>> [1 -2 3 4 5] %unary minus
ans =
1 -2 3 4 5
>> [1 - 2 3 4 5] % binary minus
ans =
-1 3 4 5
>> [1 +2 3 4 5] % unary plus
ans =
1 2 3 4 5
>> [1 + 2 3 4 5] % binary plus
ans =
3 3 4 5
As can be seen, the extra space between the operator and the number caused Matlab to change its treatment of the operator. This also applies to programmatic values such as: [value1 -value2 ...]
.
A few months ago I presented a Matlab training course [15] at some company. One of the attendees tried to programmatically modify an Excel worksheet in a Matlab program by directly accessing certain worksheet cells as ‘a3’, ‘b3’ etc. He used a simple loop such as this:
for columnIdx = 1 : 5
cellAddress = ['a' +columnIdx-1 '3']; % should be, for example: 'c3'
Excel.Range(cellAddress).Value = someValue;
end
Can you spot why Excel croaked when the Matlab program ran?
The workaround here is to either ensure that unary operators are directly adjacent to the values, or separate the values with a comma (or a semi-colon ; ) to prevent any misunderstanding by Matlab:
>> [1, - 2, 3, 4, 5]
ans =
1 -2 3 4 5
>> [1; - 2; 3; 4; 5]
ans =
1
-2
3
4
5
Categories: Low risk of breaking in future versions, Stock Matlab function
Article printed from Undocumented Matlab: https://undocumentedmatlab.com
URL to article: https://undocumentedmatlab.com/articles/matlab-numerical-gotchas
URLs in this post:
[1] Image: http://www.mezzacotta.net/garfield/
[2] Inverse-trig of “invalid” values: http://undocumentedmatlab.com/blog/matlab-numerical-gotchas/#inv-trig
[3] Numeric (in)equalities: http://undocumentedmatlab.com/blog/matlab-numerical-gotchas/#equality
[4] Cube root returns a complex number: http://undocumentedmatlab.com/blog/matlab-numerical-gotchas/#cube-root
[5] Usage of “+” and “-” in arrays: http://undocumentedmatlab.com/blog/matlab-numerical-gotchas/#arrays
[6] defined mathematically: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
[7] Matlab FAQ: http://matlab.wikia.com/wiki/FAQ#Why_does_MATLAB_return_a_complex_number_for_.28-8.29.5E.281.2F3.29
[8] Official Matlab documentation on floating-point accuracy and pitfalls: http://www.mathworks.com/help/matlab/matlab_prog/floating-point-numbers.html#f2-98690
[9] Loren’s blog post on this: http://blogs.mathworks.com/loren/2006/08/23/a-glimpse-into-floating-point-accuracy/
[10] Cleve Moler’s newsletter article: http://www.mathworks.com/company/newsletters/news_notes/pdf/Fall96Cleve.pdf
[11] Wikipedia on FP numerical inaccuracies: https://en.wikipedia.org/wiki/Floating_point#Minimizing_the_effect_of_accuracy_problems
[12] Floating-point computation pitfalls article: http://hal.archives-ouvertes.fr/docs/00/28/14/29/PDF/floating-point-article.pdf
[13] What every computer scientist should know about floating-point arithmetic article: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
[14] 1-15M1N: http://www.mathworks.com/support/solutions/en/data/1-15M1N/
[15] Image: http://undocumentedmatlab.com/training/
[16] Graphic sizing in Matlab R2015b : https://undocumentedmatlab.com/articles/graphic-sizing-in-matlab-r2015b
[17] Matlab compilation quirks – take 2 : https://undocumentedmatlab.com/articles/matlab-compilation-quirks-take-2
[18] Explicit multi-threading in Matlab part 4 : https://undocumentedmatlab.com/articles/explicit-multi-threading-in-matlab-part4
[19] Internal Matlab memory optimizations : https://undocumentedmatlab.com/articles/internal-matlab-memory-optimizations
[20] Using Infiniband with Matlab Parallel Computing Toolbox : https://undocumentedmatlab.com/articles/using-infiniband-with-matlab-parallel-computing-toolbox
[21] Solving a MATLAB bug by subclassing : https://undocumentedmatlab.com/articles/solving-a-matlab-bug-by-subclassing
[22] : http://www.mathworks.com/matlabcentral/answers/38999-what-is-the-reasoning-behind-the-fact-that-min-0-nan-is-0
Click here to print.
Copyright © Yair Altman - Undocumented Matlab. All rights reserved.
9 Comments To "Matlab numerical gotchas"
#1 Comment By Walter Roberson on Facebook On October 30, 2013 @ 14:08
Let A and B be positive. Then mod(-A,B) will return B itself, if A < eps(B), instead of returning something in [0,B) as would normally be expected for mathematical modulus operator.
#2 Comment By Chandrakanth On October 31, 2013 @ 08:58
I think I have come across quite such ‘gotachas’, but this is the only one i can recollect now.
#3 Comment By Wesley Brodsky On November 1, 2013 @ 10:28
One problem I have noticed in the past, which might still exist, is one of the “creeping imaginary part”. After a long series of calculations with all real numbers, a teeny-weenie imaginary part (<10^-10 times real part) appears. This causes an error if input to a function that only accepts real numbers. This is not the same as the cube-root issue, since the real part is what I expect. I place x = real(x) at random places in the calculations to avoid this. The problem might no longer exist, but I have not experimented to find out.
Has anyone else seen this?
#4 Comment By the cyclist On November 1, 2013 @ 13:56
I find it surprising that min(0,NaN) returns a result of 0 rather than NaN.
There is some discussion of this here:
[22]
#5 Comment By Guillaume On November 6, 2013 @ 02:24
Hmm, 2e^2iπ/3 is not a cube root of -8. The correct formula for nth roots of Ae^iθ is A^1/n * e^i(θ/n + 2kπ/n). Hence, cube roots of -8 are 2e^iπ (k=1), 2e^-iπ (k=2) and 2e^iπ/3 (k=3).
#6 Comment By Yair Altman On November 6, 2013 @ 12:39
@Guillaume – thanks for the correction. It serves me right for copying wiki math without double checking. I have now corrected both the main article and the referenced FAQ one.
BTW – it appears that like me, you have also gotten an error in your comment: I believe that the correct roots are 2e^iπ, 2e^iπ/3 and 2e^-iπ/3, and not as you have stated.
🙂
#7 Comment By Daniel E. Shub On November 11, 2013 @ 05:06
Despite the documentation for the colon function I cannot help but expect x = 0:0.1:1; to be equal to [0*0.1, 1*0.1, 2*0.1, …, 10*0.1] as that is how I describe the function working. This can cause a gotcha when checking for subsets
>> x = 0:0.1:1;
>> y = 0:0.1:0.5;
>> isequal(x(1:6), y(1:6))
#8 Comment By Malcolm Lidierth On November 18, 2013 @ 09:04
Take a Java method with signatures accepting either:
a scalar int
a double[]
as input.
MATLAB’s Java reflection mechanism will pass a scalar [10] as a double[] to the second method by default. To invoke the method with an int input, it needs to be cast explicitly to an int.
#9 Comment By Sami Varjo On January 9, 2014 @ 06:35
It was “cool” to note that the weak typing favors integers in arrays. One would expect that the more weak variable would be promoted as the floating point presentation is usually default, however:
This actually happens also if you preallocate an array for a number of vectors where you assign the values:
So also here explicit type cast for myUint8Var is required for
expectedassumed behavior (R2012a). This one haunted me quite a while…