Reputation: 121
I wrote a matrix multiplication routine in C++ and used it in an eigensolver. Then I realized that my implementation has precision problems even though I already use double-precision float-points. It seems the result of my implementation doesn't have enough precision. I tested it with BLAS and found its result doesn't match with BLAS's. The simple code below illustrates the problem and has the following output.
0: 0
1: 0
2: -1.42109e-14
test: test.cpp:59: int main(): Assertion `C1[i] == C2[i]' failed.
I can simply call BLAS routines for matrix multiplication, but I want to know why this happens. I don't know if this is an architecture-related problem. I use Intel Xeon E5-4620.
BTW, I tried to define sum with "long double" and still got different results.
Thanks, Da
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <cblas.h>
#include <assert.h>
#include <vector>
void init_vec(std::vector<double> &vec)
{
for (size_t i = 0; i < vec.size(); i++)
vec[i] = ((double) random()) / ((double) random());
}
void mv(const std::vector<double> &A, const std::vector<double> &B,
std::vector<double> &C)
{
size_t num_rows = A.size() / B.size();
size_t num_cols = B.size();
for (size_t i = 0; i < num_rows; i++) {
double sum = 0;
for (size_t j = 0; j < num_cols; j++)
sum += A[j * num_rows + i] * B[j];
C[i] = sum;
}
}
int main()
{
std::vector<double> A(100 * 10);
std::vector<double> B(10);
std::vector<double> C1(100);
std::vector<double> C2(100);
init_vec(A);
init_vec(B);
mv(A, B, C1);
cblas_dgemv(CblasColMajor, CblasNoTrans, 100, 10, 1, A.data(), 100, B.data(), 1, 0, C2.data(), 1);
for (size_t i = 0; i < C1.size(); i++) {
printf("%ld: %g\n", i, C1[i] - C2[i]);
assert(C1[i] == C2[i]);
}
}
Upvotes: 0
Views: 605
Reputation: 121
I realized where the problem is now. BLAS indeed used 80-bit float-point for multiplication. The result of the code below matches with BLAS.
void mv(const std::vector<double> &A, const std::vector<double> &B,
std::vector<double> &C)
{
size_t num_rows = A.size() / B.size();
size_t num_cols = B.size();
for (size_t i = 0; i < num_rows; i++) {
double sum = 0;
for (size_t j = 0; j < num_cols; j++) {
long double tmp1 = A[j * num_rows + i];
long double tmp2 = B[j];
sum += tmp2 * tmp1;
}
C[i] = sum;
}
}
It seems 80-bit float points are only used in multiplication. The summation still uses 64-bit. I hope this can help someone in the future.
Upvotes: 3