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:
- Inverse-trig of “invalid” values
- Numeric (in)equalities
- Cube root returns a complex number
- Usage of “+” and “-” in arrays
1. Inverse-trig of “invalid” values
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 such that their range extends past the “standard” domain (-1 to +1 in the case of asin and acos):
The result is that
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
2. Numeric (in)equalities
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, 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:
- Official Matlab documentation on floating-point accuracy and pitfalls
- Loren’s blog post on this
- Cleve Moler’s newsletter article
- Wikipedia on FP numerical inaccuracies
- Floating-point computation pitfalls article
- What every computer scientist should know about floating-point arithmetic article
3. Cube root returns a complex number
This one is also an oldie from the semi-official Matlab FAQ (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.
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
4. Usage of “+” and “-” in arrays
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 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
Do you have another similar gotcha that you’ve encountered? If so, please post a comment below.
(if there’s any interest, maybe I’ll follow up with some non-numeric gotchas for next year’s Halloween…)
Happy Halloween everyone!
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.
I think I have come across quite such ‘gotachas’, but this is the only one i can recollect now.
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?
I find it surprising that min(0,NaN) returns a result of 0 rather than NaN.
There is some discussion of this here:
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).