The multiplication result by sum() and matrix multiplication are not the same
3 views (last 30 days)
Show older comments
Assume 2 random complex matrices and we want to multiply them.
A = rand(4, 1) + 1j * rand(4, 1)
B = rand(4, 8) + 1j * rand(4, 8)
C1 = sum(A.*B,1)
C2 = A.'*B
sum(C1 == C2, "all")
C1 - C2
The weird thing happens. The result is not the same.
May I ask the reason?
Which one is right?
How to prevent this from happening?
Thanks a lot!
2 Comments
Stephen23
on 30 Jul 2023
Edited: Stephen23
on 30 Jul 2023
"May I ask the reason?"
Different numeric operations performed in a different order. No surprises there.
"Which one is right?"
Neither one is more "right" than the other.
"How to prevent this from happening?"
You cannot prevent binary floating point numbers from behaving like binary floating point numbers.
You can learn that binary floating point numbers have a limited precision and that operations on them accumulate floating point error. This means that all computations that use binary floating point numbers must take that limited precision and floating point error into account, e.g. not assuming exact equivalence between two different algorithms:
This is worth reading as well:
Bruno Luong
on 30 Jul 2023
Edited: Bruno Luong
on 30 Jul 2023
Stephen23 reply should be in answers
To illustrate the order of operation mater, a simple example of suming an array of 100 elements in two different orders
for ntest = 1:10
a = rand(1,100);
s1 = sum(a);
s2 = sum(a(randperm(end)));
fprintf('s1 = %1.16f, s2 = %1.16f\n', s1, s2);
fprintf('\ts1-s2 = %1.16f\n', s1-s2);
end
You just have to accept this when working with finite precision floating point numbers and make the code robust to round-off error.
Answers (3)
Walter Roberson
on 30 Jul 2023
format long g
A = rand(4, 1) + 1j * rand(4, 1)
B = rand(4, 8) + 1j * rand(4, 8)
C1 = sum(A.*B,1)
C2 = A.'*B
sum(C1(:) == C2(:))
C1 - C2
As = sym(A, 'f')
Bs = sym(B, 'f')
C1s = sum(As.*Bs,1)
C2s = As.'*Bs
sum(C1s(:) == C2s(:))
C1s - C2s
C1s - C1
double(ans)
C2s - C2
double(ans)
This tells you that if you take the numbers and convert them to the exact equivalent symbolic fractions, and do the calculations both ways with the symbolic fractions, that the two ways come out the same. And then from the fact that neither C1 nor C2 is exactly equal to the symbolic calculation, it follows that neither of the C1 or C2 calculation is "right"
double(C1s) - C1
norm(ans)
double(C2s) - C2
norm(ans)
In my test runs, the norms of the differences are not consistently better for one version compared to the other, so you cannot really come out with a "best" solution for the two.
Why does it happen? It happens because MATLAB calls high-performance library routines, and there happens to be a specific library routine for A' * B that does the calculations internally without ever explicitly calculating A' -- but the internal order of the calculations is not necessarily going to be exactly the same compared to the routine that calculates .* -- potentially it might even involve using different hardware instructions.
4 Comments
Bruno Luong
on 30 Jul 2023
Edited: Bruno Luong
on 30 Jul 2023
How double(sym) works? For examle in this case:
x = sym(2);
ys = exp(x)
y =double(ys)
I imagine symbolic represents somehow infinite series
ys = 1 + 2/2! + ... 2^n/n! + ...
When I need y =double(ys)
how MATLAB do the conversion, obviously it cannot compute inifinite sum, so it must truncate. Does the calculation is initite precision on truncation, then find the closest FP?
Does it compute the closest FP on each terms then sum the truncated series?
Do something else?
EDIT: according to the doc https://www.mathworks.com/help/symbolic/double.html double command do internal calculation by evaluate symbolic expression in 32 digits by default. So about twice as numerical double. But it can suffer from cancellation and round-off error as well (see examole High-Precision Conversion)
Askic V
on 30 Jul 2023
In addition what Walter and Stephen said, if you have some specific application and you need to compare the floating point numbers to check whether they're equal,, I would suggest you to look for "isequal with tolerance":
1 Comment
Walter Roberson
on 30 Jul 2023
ismembertol can also be used for "isequal with tolerance", if you can restrict one of the parameters to being a scalar.
It can handle (scalar, array) [is this number approximately equal to any of these numbers?] or (array, scalar) [are any of these numbers approximately equal to this number), but by itself is not suitable for "is each array element approximately equal to the corresponding array element", which is where the File Exchange contributions are useful.
Bruno Luong
on 31 Jul 2023
Edited: Bruno Luong
on 31 Jul 2023
You cannot prevent the roundoff error to occur and operation order dependency.
Even for the same computer the result can change by setting different number of computational threads as showed here on MATLAB online server
format long
a=randn(1,1e6);
s1=sum(a);
default_nthread = maxNumCompThreads(1)
s2=sum(a);
maxNumCompThreads(default_nthread)
d = s1-s2;
fprintf('s1 = %1.16f, s2 = %1.16f\n', s1, s2);
fprintf('\ts1-s2 = %1.16f\n', s1-s2);
and sum order
s1 = sum(a);
s3 = sum(a(randperm(end)));
fprintf('s1 = %1.16f, s3 = %1.16f\n', s1, s3);
fprintf('\ts1-s3 = %1.16f\n', s1-s3);
Multiplication of matrices have infinity of methods, implementations with different ways to accumulate the result https://en.wikipedia.org/wiki/Matrix_multiplication_algorithm so it is expected that two different functions will not lead to the same numerical result.
0 Comments
See Also
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!