如何克服双精度误差?

时间:2015-03-18 18:34:24

标签: c++ precision floating-point-precision double-precision

我的功能是在保留比例的同时摆脱两个双打的小数部分:

void enlarge(double &a, double &b)
{
    while (a != trunc(a) || b != trunc(b))
    {
        a *= 10;
        b *= 10;
        //output added for debugging
        cout << "a = " << a << ", b = " << b << endl;
        cout << "trunc(a) = " << trunc(a) << ", trunc(b) = " << trunc(b) << endl;
        cout << "a == trunc(a): " << to_string(a == trunc(a)) << ", b == trunc(b): " << to_string(b == trunc(b)) << endl;
        //to see output step by step
        string asd;
        cin >> asd;
    }
}

正常工作时的输出:

a = 0.876, b = 99.9
trunc(a) = 0, trunc(b) = 99
a == trunc(a): 0, b == trunc(b): 0
a
a = 8.76, b = 999
trunc(a) = 8, trunc(b) = 999
a == trunc(a): 0, b == trunc(b): 1
a
a = 87.6, b = 9990
trunc(a) = 87, trunc(b) = 9990
a == trunc(a): 0, b == trunc(b): 1
a
a = 876, b = 99900
trunc(a) = 876, trunc(b) = 99900    //This is where it stops working
a == trunc(a): 0, b == trunc(b): 1  //Notice how 876 != 876
a
a = 8760, b = 999000
trunc(a) = 8760, trunc(b) = 999000
a == trunc(a): 0, b == trunc(b): 1

在这种情况下我该怎么办?

2 个答案:

答案 0 :(得分:1)

问题是原始数字不是完全 .876,尽管差别不大。所以你可能需要乘以10几次才能得到整数。

另外,请注意,如果数字略小于.876,则数字的1000倍会略小于876.0,截断(875.99999999 ...)为875,而非876。

您可能会考虑某个阈值,其中数字与整数“足够接近”。例如,您可以使用测试

,而不是使用a==trunc(a)
abs(a - round(a)) < 1e-6

最后,如果你想要精确,你可以在每一步乘以2,而不是乘以10.每次乘以10都会失去一点精度,因为10不是2的幂。乘以2在最多52次乘法后将产生一个整数。

答案 1 :(得分:1)

  

“我的功能摆脱两个双打的小数部分,同时保留他们的比率:”

您可能希望使用固定点值,而不是double,以使其正常工作。 Money Pattern中描述的东西,旨在克服这些问题。

一般的想法是你有一个int值,它根据你想要/需要的精度用乘法(内部)表示来保存它。因此,如果您需要3个小数点的精度,则将表示值的(内部)乘以1000,4小数点 - &gt; * 10000,aso。

如果必须处理无法保存(表示为)long long整数变量的巨大值,则还可以使用(非标准)大整数管理库。

相关问题