fmod 函数 C++ 的输出

umb*_*sar 5 c++ floating-point fmod

鉴于:

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

int main() {
    // your code goes here
    double h = .1;
    double x = 1;
    int nSteps = abs(x / h);

    double rem = fmod(x, h);
    cout<<"fmod output is "<<rem<<endl;
    if(abs(rem)<std::numeric_limits<double>::epsilon())
        cout<<"fmod output is almost near 0"<<endl;

    rem = remainder(x,h);
    cout<<"remainder output is "<<rem<<endl;
    if(abs(rem)<std::numeric_limits<double>::epsilon())
        cout<<"remainder output is almost near 0"<<endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

鉴于int(x/h) == 10,我本来希望fmod()结果接近 0,但我得到的是 .0999999999。这是一个显着的差异。余数()的结果似乎仍然可以接受。可以在http://ideone.com/9wBlva尝试代码

为什么 fmod() 结果存在显着差异?

And*_*dyG 4

您看到的问题是fmod您正在使用的版本似乎遵循cppreference中定义的实现:

double fmod(double x, double y)
{
    double result = std::remainder(std::fabs(x), (y = std::fabs(y)));
    if (std::signbit(result)) result += y;
    return std::copysign(result, x);
} 
Run Code Online (Sandbox Code Playgroud)

std::remainder计算出一个非常非常小的结果,几乎为零(当我使用 1 和 0.1 时为 -5.55112e-17,对于 2 和 0.2 时为 -1.11022e-16)。然而,重要的是结果是负数,这意味着std::signbit返回 true ,导致y被添加到结果中,有效地使结果等于y

请注意, 的文档std::fmod没有提及任何有关使用的信息std::remainder

该函数计算出的除法运算 x/y 的浮点余数恰好是值 x - n*y,其中 n 是截去小数部分的 x/y。

因此,如果您自己计算该值,您最终会得到零(即使您使用std::round结果而不是纯整数截断)

x当为 2 和y为 0.2时,我们会看到类似的问题

double x = 2;
double y = .2;

int n = static_cast<int>(x/y);
double result = x - n*y;
std::cout << "Manual: " << result << std::endl;
std::cout << "fmod: " << std::fmod(x,y) << std::endl;
Run Code Online (Sandbox Code Playgroud)

输出(gcc 演示)是

手动:0
fmod:0.2

然而,这个问题并不仅仅局限于 gcc;我也在 MSVC 和 clang 中看到它。在 clang 中,如果使用float代替 ,有时会出现不同的行为double

这个非常小的负值std::remainder来自这样的事实:0.1 和 0.2 都无法在浮点数学中精确表示。如果将 x 和 y 更改为 2 和 0.25,那么一切都很好。