我正在使用 GCC 在 C++ 中制作更快的 mod(x,2) 函数(使用 -O3 -ffast-math 编译),并发现 GCC 和 Octave 之间的结果存在差异:
float fast_fmod2(float x){ // over 50x faster than std::fmod(x, 2.0f)
x *= 0.5f;
return 2.0f * ( x - std::floor(x));
}
Run Code Online (Sandbox Code Playgroud)
结果(mod(输入,2.0f)):
Input : -7.8539786
std::fmod() : -1.853978633881
Octave mod(): 0.146021366119
fast_fmod2 : 0.146021366119
...
Input : 7.8539805
std::fmod() : 1.853980541229
Octave mod(): 1.853980541229
fast_fmod2 : 1.853980541229
Run Code Online (Sandbox Code Playgroud)
我还检查了其他几个数学软件,看起来至少 Sollya 和 Wolfram|Alpha 支持 Octave 结果,并且之前提到的记录了与 Octave 相同的函数定义。
GCC将 mod 函数定义为:
mod(A, P) = A - (int(A/P) * P)
Run Code Online (Sandbox Code Playgroud)
mod(a, b) = a - (b * floor(a / b))
Run Code Online (Sandbox Code Playgroud)
由于 int(a/b) 与 Floor(a/b) 的舍入方式不同,GCC 定义对负 A 给出了不同的答案。
>> int16(-2.19/2)
ans = -1
>> floor(-2.19/2)
ans = -2
Run Code Online (Sandbox Code Playgroud)
这是 GCC 版本中的错误还是差异背后的其他原因?
我假设你的意思std:fmod是代替(官方 C++ 标准中std::mod没有)std::mod
造成这种差异的原因是std::fmod它的作用与您想象的不同。
std::fmod计算余数而不是算术模数。
\n\n计算除法运算的浮点余数
\nx/y
如果您想要算术模数,则需要使用std::remainder:
\n\n计算浮点除法运算的 IEEE 余数
\nx/y。\n此函数计算出的除法运算的 IEEE 浮点余数x/y恰好是值x - n*y,其中该值n是最接近精确值 的整数值x/y。当 时|n-x/y| = \xc2\xbd,该值n被选择为偶数。
这将在您的示例中产生预期结果:
\nstd::cout << std::remainder(-7.8539786f, 2.0f) << std::endl; // 0.146021\nRun Code Online (Sandbox Code Playgroud)\n\n所以回答你的问题:这不是一个错误,这是有意的行为。
\n这些函数只是在 C++ 中以不同的方式命名(尽管命名方案有点令人困惑)
mod==C++std::reminderrem==C++std::fmod