不能在双精度浮点数上使用模运算?

241

我有一个用g++编译的C++程序。我尝试将两个double作为操作数应用于模运算,但是我遇到了以下错误:

error: invalid operands of types 'double' and 'double' to binary 'operator%'

这是代码:

int main() {
    double x = 6.3;
    double y = 2;
    double z = x % y;
}

17
正如已经提到的,fmod() 提供了所需的函数。但是尚未注意到的是,需要意识到在 fmod 的第二个操作数中的舍入误差可能会导致意外行为。例如,fmod(1, 0.1) 在数学上应该是零,但实际上将近等于0.1。误差的程度随商的大小而增加。例如,fmod(9E14, 0.1) 的计算结果约为0.05,从数学上讲完全错误。 - supercat
1
@supercat 更多的细节会让人感到惊喜。我认为我知道背后发生了什么导致你所说的是真实的,但是看到你所说的真正原因会很好;看到它在幕后是如何运作的将会非常有趣(我认为我理解了,但是也可能错了)。 - RastaJedi
4
浮点数表示二的幂的精确整数倍或分数。例如,整数0.1恰好是3602879701896397/36028797018963968(后一个值是2的幂)。 fmod(x,0.1)将通过该精确分数对x进行除法并取余数,而不是除以数字值“十分之一”。 - supercat
1
可能是重复问题:为什么模数除法(%)只适用于整数? - Paul R
5个回答

336

%运算符用于整数。你需要使用fmod()函数

#include <cmath>

int main()
{
    double x = 6.3;
    double y = 2.0;
    double z = std::fmod(x,y);

}

1
我正在使用C++为Qt编程,fmod在那里有一个错误。 fmod(angle,360)的结果可能是360(什么?!) - Paul
9
@Paul: 那可能不是一个 bug。如果 angle 是,比如说,359.9999999,那么 anglefmod(angle, 360) 都很可能被显示为 360。(根据需要添加更多的 9)。尝试以 50 位有效数字打印这些值。 - Keith Thompson
@Paul Qt的QString :: format会四舍五入。如果这很关键,您要么必须自己进行四舍五入,要么检查输出是否为“360”,并将其替换为您自己的值。 - jfh
1
@Rohan main(void)C++中不是必需的(它是C的遗留),而且您的编辑也没有改进任何其他内容。我已将其恢复。 - Ayxan Haqverdili
2
这对负数不起作用。std::fmod(-1.0, 2 * M_PI) 返回 -1.0,我期望的是大约 5.28 - Donald Duck

40

fmod(x, y)是你需要使用的函数。


8

您可以实现自己的模运算函数来完成此操作:

double dmod(double x, double y) {
    return x - (int)(x/y) * y;
}

那么您可以简单地使用dmod(6.3, 2)来获取余数,即0.3


仍然不是一个完美的解决方案。x=10.499999999999998,y=0.69999999999999996返回0.69999999999999929而不是0.0。 - tarpista
2
@tarpista:这在使用浮点数时是可以预料的。 - Eric Duminil

6
使用 <cmath> 中的 fmod()。如果您不想包含 C 头文件:
template<typename T, typename U>
constexpr double dmod (T x, U mod)
{
    return !mod ? x : x - mod * static_cast<long long>(x / mod);
}

//Usage:
double z = dmod<double, unsigned int>(14.3, 4);
double z = dmod<long, float>(14, 4.6);
//This also works:
double z = dmod(14.7, 0.3);
double z = dmod(14.7, 0);
double z = dmod(0, 0.3f);
double z = dmod(myFirstVariable, someOtherVariable);

6
为什么您不想包含 C 头文件?那是它存在的原因。 - Keith Thompson
在答案中放置代码没有任何问题。谢谢。 - undefined

0

这里是一个可以适用于正负数的函数:

#include <cmath>

inline double nmod(double x, double y) {
    return std::fmod(std::fmod(x, y) + y, y);
}

如果x是负数或正数,它将始终返回一个正数。

请注意,如果y是负数,则返回值将为负数。如果您需要一个无论如何都返回正数的函数,则需要在y上使用std::fabs(),即:

inline double nmod2(double x, double y) {
    return std::fmod(std::fmod(x, y) + std::fabs(y), y);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接