为什么不同编程语言中的模数运算结果不同?

23
Perl
print 2 % -18;
-16

Tcl

puts [expr {2 % -18}]
-16

但VBScript

wscript.echo 2 mod -18
2

为什么会有这样的差别?


谢谢分享。我现在已经在Python、Ruby、Javascript和bash中进行了测试。JS和bash都显示为2,而其他的则显示为-16。 - PEZ
模数运算主要属于计数数字,并在此之外任意定义。 - Axeman
4个回答

31

维基百科答案对此相当有帮助。

简短地说,任何整数都可以定义为

a = qn + r

其中所有这些字母都是整数,并且

0 <= |r| < |n|。

几乎每种编程语言都要求 (a/n) * n + (a%n) = a。因此,模数的定义几乎总是取决于整数除法的定义。当涉及到负数的整数除法时,有两个选择:2/-18 = 0 或者 2/-18 = -1。根据您所使用的语言的真实情况,通常会改变 % 运算符。

这是因为 2 = (-1) * -18 + (-16) 和 2 = 0 * -18 + 2。

对于 Perl 来说,情况很复杂。 手册页面上写道:"请注意,当 use integer 处于作用域中时,“%”将直接访问由您的 C 编译器实现的模数运算符。该运算符对于负操作数并没有那么好的定义,但它将执行更快。" 因此,如果 use integer 处于作用域内,Perl(如 C)可以选择其中任一选项。 如果 use integer 不在作用域内,则手册上写道:"如果 $b 为负,则 $a % $b 是 $a 减去不小于 $a 的最小倍数的 $b(即结果将小于或等于零)。


关于“可以做任何事情”的问题,并不是这样的。如果使用整数,则它只能做C语言可以做的事情,并且C标准明确规定 (a/n) + (a%n) = a,但对于整数除法,允许任一选择。 - ysth
(100/2)+(100%2) != 100 ... 我有什么遗漏吗? - B T
@B T,糟糕,我漏掉了一个n的因素,感谢你指出,我会修复它。 - Nick Fortescue

9

维基百科的“模运算”页面已经很好地解释了它。我不会试图在这里做得更好,因为我可能会犯一个微妙但重要的错误。

关键是您可以以不同的方式定义“余数”或“模数”,不同的编程语言选择不同的选项来实现。


我认为你的原始答案正中要害。有时候 '%' 表示“余数”,有时候表示“模运算”,这两者并不相同。 - Bill the Lizard
@Bill:我不确定是否要删除它,但这归结于“余数”和“模数”的定义,这些定义也显然不像人们希望的那样清晰明了。 - Jon Skeet

4

在一个数和一个除数中,如果有一个是负数,那么可以有至少两种方式将它们分开成商和余数,使得商*除数+余数=数:你可以将商向负无穷舍入,也可以向零舍入。

许多编程语言只选择其中一种。

我不禁要指出Common Lisp提供了这两种方式。


3

当然,Python会明确地告诉你

>>> divmod(2,-18)
(-1, -16)

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