如何克服双精度不准确?

How to overcome double precision inaccuracy?

我的函数是去掉两个双打的小数部分,同时保留它们的比例:

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

遇到这种情况我该怎么办?

问题是原始数字并非完全.876,尽管两者相差不大。所以你可能需要多次乘以 10 才能得到一个整数。

另请注意,如果数字略小于 .876,则数字的 1000 倍将略小于 876.0,trunc(875.99999999...) 是 875,而不是 876。

您可能会考虑一些阈值,其中数字是 "close enough" 到整数。例如,您可以使用 test

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

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

"My function to get rid of fractional part of two doubles while retaining their ratio:"

您可能想要使用定点值而不是 double 来使其正常工作。 Money Pattern 中描述的内容旨在克服此类问题。

一般的想法是你有一个 int 值,它根据你 want/need 的精度通过乘法(内部)表示来保存它。因此,如果您需要 3 个小数点的精度,您可以(在内部)将表示值乘以 1000、4 个小数点 -> * 10000,aso.

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