为什么模除法(%)只适用于整数?

我最近遇到了 一个问题,它可以很容易地用模除法求解,但输入是一个浮点数:

给定一个周期函数(例如 sin)和一个只能在周期范围内计算的计算机函数(例如[-π,π ]) ,创建一个可以处理任何输入的函数。

“显而易见”的解决方案是这样的:

#include <cmath>


float sin(float x){
return limited_sin((x + M_PI) % (2 *M_PI) - M_PI);
}

为什么这个不起作用? 我得到这个错误:

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

有趣的是,它确实可以在 Python 中工作:

def sin(x):
return limited_sin((x + math.pi) % (2 * math.pi) - math.pi)
163770 次浏览

You're looking for fmod().

I guess to more specifically answer your question, in older languages the % operator was just defined as integer modular division and in newer languages they decided to expand the definition of the operator.

EDIT: If I were to wager a guess why, I would say it's because the idea of modular arithmetic originates in number theory and deals specifically with integers.

The modulo operator % in C and C++ is defined for two integers, however, there is an fmod() function available for usage with doubles.

try fmod

The % operator gives you a REMAINDER(another name for modulus) of a number. For C/C++, this is only defined for integer operations. Python is a little broader and allows you to get the remainder of a floating point number for the remainder of how many times number can be divided into it:

>>> 4 % math.pi
0.85840734641020688
>>> 4 - math.pi
0.85840734641020688
>>>

Because the normal mathematical notion of "remainder" is only applicable to integer division. i.e. division that is required to generate integer quotient.

In order to extend the concept of "remainder" to real numbers you have to introduce a new kind of "hybrid" operation that would generate integer quotient for real operands. Core C language does not support such operation, but it is provided as a standard library fmod function, as well as remainder function in C99. (Note that these functions are not the same and have some peculiarities. In particular, they do not follow the rounding rules of integer division.)

I can't really say for sure, but I'd guess it's mostly historical. Quite a few early C compilers didn't support floating point at all. It was added on later, and even then not as completely -- mostly the data type was added, and the most primitive operations supported in the language, but everything else left to the standard library.

The constraints are in the standards:

C11(ISO/IEC 9899:201x) §6.5.5 Multiplicative operators

Each of the operands shall have arithmetic type. The operands of the % operator shall have integer type.

C++11(ISO/IEC 14882:2011) §5.6 Multiplicative operators

The operands of * and / shall have arithmetic or enumeration type; the operands of % shall have integral or enumeration type. The usual arithmetic conversions are performed on the operands and determine the type of the result.

The solution is to use fmod, which is exactly why the operands of % are limited to integer type in the first place, according to C99 Rationale §6.5.5 Multiplicative operators:

The C89 Committee rejected extending the % operator to work on floating types as such usage would duplicate the facility provided by fmod

The % operator does not work in C++, when you are trying to find the remainder of two numbers which are both of the type Float or Double.

Hence you could try using the fmod function from math.h / cmath.h or you could use these lines of code to avoid using that header file:

float sin(float x) {
float temp;
temp = (x + M_PI) / ((2 *M_PI) - M_PI);
return limited_sin((x + M_PI) - ((2 *M_PI) - M_PI) * temp ));

}

"The mathematical notion of modulo arithmetic works for floating point values as well, and this is one of the first issues that Donald Knuth discusses in his classic The Art of Computer Programming (volume I). I.e. it was once basic knowledge."

The floating point modulus operator is defined as follows:

m = num - iquot*den ; where iquot = int( num/den )

As indicated, the no-op of the % operator on floating point numbers appears to be standards related. The CRTL provides 'fmod', and usually 'remainder' as well, to perform % on fp numbers. The difference between these two lies in how they handle the intermediate 'iquot' rounding.

'remainder' uses round-to-nearest, and 'fmod' uses simple truncate.

If you write your own C++ numerical classes, nothing prevents you from amending the no-op legacy, by including an overloaded operator %.

Best Regards