给定fmod(1001.0, 0.0001)的预期结果,0.00009999999995提供的0精度似乎很低(10-5)。
根据cppreferencefmod()是可以使用remainder()实现的,但是remainder(1001.0, 0.0001)可以提供-4.796965775988316e-14(仍然远离double精度,但是比10-5好得多)。
为什么fmod精度很大程度上取决于输入参数?正常吗
MCVE:

#include <cmath>
#include <iomanip>
#include <iostream>
using namespace std;

int main() {
    double a = 1001.0, b = 0.0001;
    cout << setprecision(16);
    cout << "fmod:      " << fmod(a, b) << endl;
    cout << "remainder: " << remainder(a, b) << endl;
    cout << "actual:    " << a-floor(a/b)*b << endl;
    cout << "a/b:       " << a / b << endl;
}
输出:
fmod:      9.999999995203035e-05
remainder: -4.796965775988316e-14
actual:    0
a/b:       10010000
(使用和不使用优化,GCC,Clang,MSVC的结果相同)
Live demo

最佳答案

如果我们将您的程序修改为:

#include <cmath>
#include <iomanip>
#include <iostream>

int main() {
    double a = 1001.0, b = 0.0001;
    std::cout << std::setprecision(32) << std::left;
    std::cout << std::setw(16) << "a:" << a << "\n";
    std::cout << std::setw(16) << "b:" << b << "\n";
    std::cout << std::setw(16) << "fmod:" << fmod(a, b) << "\n";
    std::cout << std::setw(16) << "remainder:" << remainder(a, b) << "\n";
    std::cout << std::setw(16) << "floor a/b:" << floor(a/b) << "\n";
    std::cout << std::setw(16) << "actual:" << a-floor(a/b)*b << "\n";
    std::cout << std::setw(16) << "a/b:" << a / b << "\n";
    std::cout << std::setw(16) << "floor 10009999:" << floor(10009999.99999999952) << "\n";
}
它输出:
a:              1001
b:              0.00010000000000000000479217360238593
fmod:           9.9999999952030347032290447106817e-05
remainder:      -4.796965775988315527911254321225e-14
floor a/b:      10010000
actual:         0
a/b:            10010000
floor 10009999: 10010000
我们可以看到0.0001不能表示为double,因此b实际上设置为0.00010000000000000000479217360238593
这导致a/b10009999.9999999995203034224,因此意味着fmod应该返回1001 - 10009999*0.00010000000000000000479217360238593,即9.99999999520303470323e-5
(在speedcrunch中计算的数字,因此可能与IEEE double值不完全匹配)
您的“实际”值不同的原因是floor(a/b)返回的10010000不是fmod使用的确切值10009999,这本身是由于10009999.99999999952无法表示为 double ,因此在传递给地板之前将其四舍五入为10010000

关于c++ - std::fmod糟糕的 double ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/64211652/

10-15 13:39