给定fmod(1001.0, 0.0001)
的预期结果,0.00009999999995
提供的0
精度似乎很低(10-5)。
根据cppreference,fmod()
是可以使用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/b
为10009999.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/