C# 矩阵乘法的精度误差

C# 矩阵乘法的精度误差,c#,matrix,floating-point,double,precision,C#,Matrix,Floating Point,Double,Precision,在我的程序中编码一个矩阵乘法,我会得到精度错误(对于大型矩阵,结果不准确) 这是我的密码。当前对象的数据存储在扁平数组中,一行接一行。另一个矩阵B的数据存储在一个扁平数组中,一列接一列(所以我可以使用指针算法) protecteddouble[,]乘法(IMatrix B) { int columns=B.columns; int行=行; int size=列; 双精度[,]结果=新的双精度[行,列]; 对于(int row=0;row而不是双< /代码>。但是,这可能会导致性能下降,因此请先进

在我的程序中编码一个矩阵乘法,我会得到精度错误(对于大型矩阵,结果不准确)

这是我的密码。当前对象的数据存储在扁平数组中,一行接一行。另一个矩阵B的数据存储在一个扁平数组中,一列接一列(所以我可以使用指针算法)

protecteddouble[,]乘法(IMatrix B)
{
int columns=B.columns;
int行=行;
int size=列;
双精度[,]结果=新的双精度[行,列];
对于(int row=0;row
事实上,代码有点复杂:我对几个块进行乘法运算(因此,我没有让I从0到大小,而是从localStart到localStop),然后对结果矩阵求和

我的问题:对于一个大矩阵,我得到了精度误差:

NUnit.Framework.AssertionException: Error at (0,1)
    expected: <6.4209571409444209E+18>
     but was: <6.4207619776304906E+18>
NUnit.Framework.AssertionException:在(0,1)处出错
预期:
但是:

有什么想法吗?

哼哼,它并不能真正解决你的问题,但在NUnit中,你可以允许有一个精度误差并选择这个ε的值,它并不能真正解决你的问题,但在NUnit中,你可以允许有一个精度误差并选择这个ε的值作为起点,使用
double
无处不在而不是
float

作为起点,使用
double
无处不在而不是
float

我最初说过应该将
float
转换为
double
。然而,正如您所指出的,这将破坏您的算法

你可以试试:

value += (double)*(mePtr++) * (double)*(bPtr++);
当前代码的一个问题是乘法是以
浮点
精度进行的,然后添加到
双精度
。先向
double
施放将在一定程度上有所帮助

使用中间
double
变量可能更清晰,但这取决于您


如果这不给你想要的精度,那么你需要考虑使用<代码>十进制< /代码>而不是<代码>双< /代码>。但是,这可能会导致性能下降,因此请先执行一些基准测试。

我最初说过,您应该将
浮点值
转换为
双倍值
。然而,正如您所指出的,这将破坏您的算法

你可以试试:

value += (double)*(mePtr++) * (double)*(bPtr++);
当前代码的一个问题是乘法是以
浮点
精度进行的,然后添加到
双精度
。先向
double
施放将在一定程度上有所帮助

使用中间
double
变量可能更清晰,但这取决于您


如果这不给你想要的精度,那么你需要考虑使用<代码>十进制< /代码>而不是<代码>双< /代码>。但是,这可能会导致性能下降,因此请先进行一些基准测试。

至少,您应该在整个过程中使用双精度测试。浮动是非常不精确的。

至少,你应该在整个过程中使用双精度。浮动非常不精确。

也许你所要做的就是使用浮动。但是你可以用浮点运算精确地得到一个特定的结果。

也许你所要做的就是使用。但是你可以用浮点数学精确地得到一个特定的结果。

这是一种称为“矩阵蠕变”的现象,如果你不一致地规范化你的矩阵,这种现象会在矩阵操作过程中逐渐发生。

这是一种称为“矩阵蠕变”的现象这在矩阵操作过程中会逐渐发生,如果你不一致地规范化你的矩阵。

结果只是。。。虫子。结果不是:

float* mePtr = ptrThis + row*rows;
float* bPtr = ptrB + col*columns;
我的行的正确索引器是:

float* mePtr = ptrThis + row * size;
float* bPtr = ptrB + col * size;

很抱歉,这里的答案不是很花哨。但是谢谢你的帮助

结果只是。。。虫子。结果不是:

float* mePtr = ptrThis + row*rows;
float* bPtr = ptrB + col*columns;
我的行的正确索引器是:

float* mePtr = ptrThis + row * size;
float* bPtr = ptrB + col * size;

很抱歉,这里的答案不是很花哨。但是谢谢你的帮助

当然,浮点数的精确度不如双倍点数。但是所有(好吧,几乎所有,我的统计是不准确的)浮点数都是不精确的。@High,double比float多29位精度。这使它们的精确度提高了约1亿倍。@Marcelo--确实如此。而且,我们中的许多人在进行严肃的数字运算时,不得不担心长时间运行的计算会导致精度下降。将浮动更改为双倍只会推迟问题,而不会消除问题。当然,有时它会将问题推迟到计算完成之后。当然,浮点数的精度不如双精度,这是肯定的。但是所有(好吧,几乎所有,我的统计是不准确的)浮点数都是不精确的。@High,double比float多29位精度。这使它们的精确度提高了约1亿倍。@Marcelo--确实如此。而且,我们中的许多人在进行严肃的数字运算时,不得不担心长时间运行的计算会导致精度下降。将浮动更改为双倍只会推迟问题,而不会消除问题。当然,有时它会将问题推迟到计算完成之后。是的,但是将mePtr和bPtr转换为double,我仍然可以使用mePtr++吗?或者这将只使用数组中一半的元素吗?b、 数据是float[],如果mePtr是指向b.data[0]的双指针,那么mePtr++的右边不是8个字节,而是b.data[2]而不是b.data[