fmod函数的输出c ++
output of fmod function c++
鉴于:
#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;
}
鉴于 int(x/h) == 10
,我本以为 fmod()
结果接近 0,但我得到的是 .0999999999。这是一个显着的差异。 remainder() 的结果似乎仍然可以接受。可以在 http://ideone.com/9wBlva
试用代码
为什么 fmod() 结果有如此显着差异?
您看到的问题是您使用的 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);
}
std::remainder
计算出一个非常非常小的结果,几乎为零(对我使用 1 和 0.1 时为 -5.55112e-17,对 2 和 0.2 使用 -1.11022e-16)。然而,重要的是结果是 negative,这意味着 std::signbit
returns 为真,导致 y
被添加到结果中,有效地使结果等于 y
.
请注意,std::fmod
的文档没有说明如何使用 std::remainder
:
The floating-point remainder of the division operation x/y calculated by this function is exactly the value x - n*y, where n is x/y with its fractional part truncated.
所以如果你自己计算这个值,你最终会得到零(即使你在结果上使用 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;
输出 (gcc demo) 是
Manual: 0
fmod: 0.2
然而,问题并不仅限于 gcc;我也在 MSVC 和 clang 中看到它。在 clang 中,如果使用 float
而不是 double
,有时会有不同的行为。
这个来自 std::remainder
的非常小的负值是因为 0.1 和 0.2 都不能在浮点数学中精确表示。如果将 x 和 y 更改为 2 和 0.25,则一切正常。
鉴于:
#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;
}
鉴于 int(x/h) == 10
,我本以为 fmod()
结果接近 0,但我得到的是 .0999999999。这是一个显着的差异。 remainder() 的结果似乎仍然可以接受。可以在 http://ideone.com/9wBlva
为什么 fmod() 结果有如此显着差异?
您看到的问题是您使用的 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);
}
std::remainder
计算出一个非常非常小的结果,几乎为零(对我使用 1 和 0.1 时为 -5.55112e-17,对 2 和 0.2 使用 -1.11022e-16)。然而,重要的是结果是 negative,这意味着 std::signbit
returns 为真,导致 y
被添加到结果中,有效地使结果等于 y
.
请注意,std::fmod
的文档没有说明如何使用 std::remainder
:
The floating-point remainder of the division operation x/y calculated by this function is exactly the value x - n*y, where n is x/y with its fractional part truncated.
所以如果你自己计算这个值,你最终会得到零(即使你在结果上使用 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;
输出 (gcc demo) 是
Manual: 0
fmod: 0.2
然而,问题并不仅限于 gcc;我也在 MSVC 和 clang 中看到它。在 clang 中,如果使用 float
而不是 double
,有时会有不同的行为。
这个来自 std::remainder
的非常小的负值是因为 0.1 和 0.2 都不能在浮点数学中精确表示。如果将 x 和 y 更改为 2 和 0.25,则一切正常。