当我在 c++ 中使用 fmod(0.6,0.2) 时,它返回 0.2
我知道这是由浮点精度引起的,但看来我此时必须获得两个双精度数的余数
非常感谢此类问题的任何解决方案
数学值
0.6
和0.2
无法用二进制浮点数精确表示。
这个演示程序将向您展示发生了什么:
#include <iostream>
#include <iomanip>
#include <cmath>
int main() {
const double x = 0.6;
const double y = 0.2;
std::cout << std::setprecision(60)
<< "x = " << x << "\n"
<< "y = " << y << "\n"
<< "fmod(x, y) = " << fmod(x, y) << "\n";
}
我的系统(很可能在你的系统上)的输出是:
x = 0.59999999999999997779553950749686919152736663818359375
y = 0.200000000000000011102230246251565404236316680908203125
fmod(x, y) = 0.1999999999999999555910790149937383830547332763671875
根据您传递的参数,
fmod()
返回的结果是正确的。
如果您需要其他结果(我想您正在期待
0.0
),您将不得不做一些不同的事情。有多种可能性。您可以检查结果是否与 0.2
存在很小的差异,或者您可以使用整数算术进行计算(例如,如果您正在处理的所有数字都是 0.1
或 的倍数) 0.01
)。
我认为最好的选择是使用余数函数而不是 fmod。对于给定的示例,它将返回一个非常小的数字,而不是 0.2。您可以利用这一事实,通过四舍五入到一定的精度级别来假设余数为 0。
你是对的,问题是舍入错误。试试这个代码:
#include <stdio.h>
#include <math.h>
int main()
{
double d6 = 0.6;
double d2 = 0.2;
double d = fmod (d6, d2);
printf ("%30.20e %30.20e %30.20e\n", d6, d2, d);
}
当我在 gcc 4.4.7 中运行它时,输出是:
5.99999999999999977796e-01 2.00000000000000011102e-01 1.99999999999999955591e-01
至于如何“修复”它,我不太清楚你到底想做什么,不知道该建议什么。总会有一些数字无法用浮点精确表示,所以这种行为是不可避免的。
有关问题领域的更多信息将有助于获得更好的建议。例如,如果您正在使用的数字始终只有小数点后一位数字,并且足够小,则只需乘以 10,四舍五入到最接近的整数(或 long 或 long long),然后使用 % 运算符而不是fmod 函数。如果您想查看 fmod 的结果是否为 0.0,只需接受接近 0.2 的结果(在本例中),就好像它接近 0 一样。
这不是一个真正的解决方案,而是一个简单的解决方法。 Matlab 的文档中有一个简单的公式,它显示了它的 mod 函数如何几乎正确地计算浮点模(我的意思是尽管存在浮点错误,但它非常接近预期结果。)。该公式仅由几个浮点运算组成:
x - y * floor(x / y)
。
这是一个带有一些测试用例的小程序。
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <fenv.h>
#include <float.h>
static inline double mod(double x, double y)
{
return x - y * floor(x / y);
}
double test_parameters[][3] =
{
{ 1.0, 0.3, 1.0 },
{ 10.0, 0.0002, 0.0 },
{ 5.0, 2.0, 1.0 },
{ 6.1, 3.0, 0.1 },
{ 1.0, 0.7, 0.3 },
};
int main()
{
for (size_t i = 0; i < sizeof(test_parameters) / sizeof(test_parameters[0]); i++)
{
double x = test_parameters[i][0];
double y = test_parameters[i][1];
double e = test_parameters[i][2];
double m = mod(x, y);
printf("mod(%lf, %lf) == %lf: %s\n", x, y, m, m - e < DBL_EPSILON ? "true": "false");
}
}