为什么operator%被称为“模数”运算符而不是“余数”运算符?

时间:2012-02-14 21:45:56

标签: c++ operators standards modulus

今天上班时我和我的一位同事进行了一次有趣的讨论。当他遇到以下情况时,他感到很惊讶:

assert(-1 % 10 == -1)  //Expecting 9

所以当他来问我这件事的时候,我告诉他“好吧,这是有道理的。当你将-1除以10时,你得到0还剩-1。然而他的论点是模数算子应该是坚持“永远积极”的模式。我做了一点研究,发现他所指的模数看起来像这样:

  

设q是a和n的整数商。设r为余数。然后:

     

a = n * q + r

I 使用的定义似乎是模数的Knuth版本,它是:

  

设q是除以n的最低值。设r为余数。然后:

     

r = a - n * q

所以,我的问题是为什么它最终在FORTRAN标准(以及随后的C标准)中使模数运算符截断为0?把它称为“模数”而不是“余数”似乎是一种误称(在数学中,答案应该是9)。这与硬件如何进行划分有关吗?

供参考:

TLDR;硬件是模数运算符向0截断的原因吗?

3 个答案:

答案 0 :(得分:16)

  

对我来说,把它称为“模数”而不是“余数”似乎是一种误称(在数学中,答案确实应该是9)。

C将其称为%运算符,并将其结果调用为余数。 C ++从C复制它。两种语言都不称它为模数运算符。这也解释了为什么余数为负数:因为/运算符截断为0,(a / b) * b + (a % b)应该等于a

编辑:DavidRodríguez正确地指出C ++ 确实定义了一个模板类std::modulus,它调用了operator%。在我看来,这个班级的名字很差。挖掘一下,它继承自STL,它现在已经命名为现在。 STL的下载说“STL是在SGI MIPSproTM C ++ 7.0,7.1,7.2和7.2.1上开发的。”,据我所知,实际上没有编译器和硬件,MIPSpro将分区传递给CPU和MIPS硬件截断为0,这意味着std::modulus一直被错误命名。

答案 1 :(得分:5)

%是C和C ++中的余数运算符。

在C ++标准中,它被称为%运算符,它产生除了的余数。在C标准中,它被称为 %运算符,因为C99它实际上是一个余数运算符。模数和余数运算符在负值方面有所不同。

%运算符在C和C ++中使用a == (a / b * b) + a % b定义。

自C99起,C中整数除法向0截断。在C89中,它是实现定义的(并且%可以是C89中的模运算符)。对于整数除法,C ++也会向零执行截断。

当截断朝零时,%是余数运算符,结果的符号是被除数的符号。截断到负无穷大时,%是模运算符,结果的符号是除数的符号。

关于C改变了关于截断的整数除法的实现定义行为的原因,来自C委员的Doug Gwyn说:

  

C99强加了Fortran兼容的要求,试图吸引更多的Fortran程序员并帮助将Fortran代码转换为C.

C99理由说关于截断为零整数除法:

  

然而,在Fortran中,结果将始终截断为零,并且开销似乎可以被数字编程社区接受。因此,C99现在需要类似的行为,这应该有助于将代码从Fortran移植到C。

gcc中,C89中的实现行为始终是截断为零。

所以%是C99,C ++和Java中的余数运算符,但不是所有编程语言中的余数运算符。在Ruby和Python中,%实际上是模运算符(整数除法是在这些语言中对负无穷大进行的)。 Haskhell和Scheme有两个独立的运算符:Haskell为modrem,Scheme为moduloremainder

答案 2 :(得分:1)

我担心这个问题源于对数学的误解。同余模n是等价关系,因此它只定义等价类。因此, 正确地说'在数学中,答案确实应该是9 ',因为它可能也是19,29,等等。当然它可以是-1或-11。数n的类的无限元素是n≡-1 mod(10)。

http://en.wikipedia.org/wiki/Modular_arithmetic

http://en.wikipedia.org/wiki/Congruence_relation

所以,正确的问题可能是:数字类中≡-1 mod(10)的哪个元素将是C ++中-1%10的结果?答案是:余数除以-1乘以10.没有神秘感。

PS你对模数和Knuth的定义是,等等......:)