避免双精度数字的四舍五入

Avoiding the rounding of double numbers

本文关键字:四舍五入 数字 双精度      更新时间:2023-10-16

我正在尝试编写一个函数,该函数使用最小二乘方法从数据表中计算回归线,但是我的代码遇到了一些严重的问题。

我的第一个问题是,我不知道为什么我的"线性回归"函数要舍入迭代的结果,即使我尝试使用其他"更大"的类型也是如此。

我的第二个问题是,我的代码的最后一部分给了我y截距(b)和斜率(a)的错误结果,我认为这可能是转换问题,但我不太确定。如果是这种情况,我应该怎么做才能避免它?

void RegLin (const vector<double>& valuesX, const vector<double>& valuesY, vector<double>& PenOrd) {
unsigned int N=valuesX.size();
long double SomXi{0};
for (unsigned i=0; i<N; ++i){
SomXi+=valuesX.at(i);
}
long double SomXiXi{0};             
for (unsigned i=0; i<N; ++i){              //Here is a problem (number rounded) Expected value: 937352,25 / Given value: 937352
SomXiXi+=(valuesX.at(i))*(valuesX.at(i));
}
long double SomYi{0};
for (unsigned i=0; i<N; ++i){
SomYi+=valuesY.at(i);
}
long double SomXiYi{0};
for (unsigned i=0; i<N; ++i){              //Here is the same problem Excepted value: 334107,41 / Given value: 334107
SomXiYi+=(valuesX.at(i))*(valuesY.at(i));
}
long double a=(SomYi*SomXiXi-SomXi*SomXiYi)/(N*SomXiXi-pow(SomXi,2)); //Bad result
long double b=(N*SomXiYi-SomYi*SomXi)/(N*SomXiXi-pow(SomXi,2)); //Bad result
PenOrd.push_back(a);
PenOrd.push_back(b);
return;
}

提前感谢您的支持

PS:我使用的是 g++ 和 2011 C++ 标准。

你的努力是有分点的。我是一个理论物理和数值数学的人。因此,让我与您分享一些最佳实践。

首先,我从未遇到过使用long double的需要。坚持使用double,因为如果这还不够,那么您应该考虑使用对数-对数图来进一步分析您的数据。

其次,您using unsignedint 而不是int。您永远不应该对那么多的值(即值对)进行回归工作,以至于对整数计数器使用int或最佳std::size_t是不够的。由于累积数值舍入问题,使用过多的值可能会降低准确性。因此,不要使用超过 10000 到 100 万个值,直到您有充分的理由这样做。

第三,很快,有必要不要直截了当地添加你的平方(例如,对于 SumXiXi 等),而是在实际汇总之前对你的贡献进行排序。你正确地总结它们,从最小的价值开始,然后是你对总和不断增长的贡献。这是掌握累积四舍五入问题的唯一方法。

第四,结果控制。结果可靠性的一个好迹象是可以实现的,如果你对你的工作进行两次,一次是你所做的(即使用x_iy_i - xy_i - x_iy + xy形式),然后作为第二种方法使用尚未相乘的(x_i - x)(y_i - y)公式。使用任一公式,高质量的计算都会产生非常可比的结果。

所以,也许这是做数值回归工作的弯路,希望它能有所帮助。

问候,米查

浮点数值计算的第一条规则说:"只处理相同顺序的值"。

浮点数学在例如加法(浮点)的情况下非常简单:

1e6 + 1e-6 = 1000000 + 0.000001 = 1000000.000001 = 1000000 = 1e6
^
precision limit

因此,如您所见,结果是"四舍五入"的。

由于2 种可能性,您完全有可能出现错误:

1)long double == double编译器,你得到错误的结果

2)浮点运算不能以100%的精度表示值,因此0.10 != 0.10 written as float/double

根据您正在执行的计算类型,我会建议您将值增加几次方或将数据更改为浮点数并以双倍存储值。