为什么C语言和Ruby语言中的取模运算符(%)在处理负数时表现不同?

4

我在这里运行一些代码。我尝试了-40 % 3。它给出的输出是2。当我在C中执行相同的操作时,我得到:

int i = (-40) % 3
printf("%d", i);

输出结果为

-1

两种语言在执行模运算时内部是如何运作的?

http://www.yourdailygeekery.com/2011/06/28/modulo-of-negative-numbers.html - Jens Wirth
另一个重复的问题:https://dev59.com/XW865IYBdhLWcg3wEaUx - alk
1
当前链接的问题不是重复的。那个问题是关于(int) % (unsigned int),与这个问题无关。现在https://dev59.com/-XRA5IYBdhLWcg3wyBD1更适合作为重复问题。 - Mr Lister
@MrLister;实际上,这也不是更好的重复。 - haccks
1
@haccks,为什么在倒数第二行把小写字母c改成了大写C,但是在第二行却没有?你的其他修改让这个问题变得像小学水平。如果你想编辑别人的问题,你应该在足够学好英语之后再进行。 - sawa
不同意关闭投票。问题非常清楚。 - haccks
3个回答

9

维基百科说:

给定两个正数a(被除数)和n(除数),a模n(记为a mod n)是欧几里得除法的余数。
... an为负数时,朴素定义就会失效,编程语言在如何定义这些值方面存在差异


现在的问题是为什么Ruby中的-40 % 3等于2,或者换句话说,背后的数学原理是什么?

让我们从欧几里得除法开始,它规定:

给定两个整数an,其中n ≠ 0,存在唯一的整数qr,使得a = n*q + r0 ≤ r < |n|,其中|n|表示n的绝对值。

现在注意到商的两个定义:

  1. Donald Knuth描述了向下取整的商,其中商由floor函数定义为q=floor(a/n),余数r为:

enter image description here

这里商(q)始终向下舍入(即使它已经是负数),余数(r)与除数具有相同的符号

  1. 一些实现将商定义为:

q = sgn(a)floor(|a| / n),其中sgn是符号函数。

余数(r)与被除数(a)具有相同的符号

现在一切都取决于q

  • 如果实现遵循定义1,并将q定义为floor(a/n),那么40 % 3的值为1,而-40 % 3的值为2。在这里,Ruby似乎是这种情况。
  • 如果实现遵循定义2,并将q定义为sgn(a)floor(|a| / n),那么40 % 3的值为1,而-40 % 3的值为-1。在这里,C和Java似乎是这种情况。

1
在Java和C中,取模运算的结果与被除数具有相同的符号,因此在您的示例中,-1是结果。
在Ruby中,它与除数具有相同的符号,因此根据您的示例,+2将是结果。

在Java和C中,模运算的结果与被除数具有相同的符号,在C89中不能保证。 - haccks
首先,我想知道为什么Ruby的结果是+2? - Aalok
1
在 Ruby 中,它与除数具有相同的符号(+ 在这里),因此它将计算为 -314 = -42 +2 = -40。但对于 C 来说,它将计算为 -313=-39 -1= -40。因此,+2 和 -1 是每种情况的余数。 - user3706295

0
在 Ruby 实现中,当分子为负数且分母为正数时,模运算符回答的问题是:“从分子中减去哪个最小的正整数可以使得分母能够整除结果?” 在所有实现中,当分子和分母都为正数时,回答的问题是:“从分子中减去哪个最小的正整数可以使得分母能够整除结果?” 因此,您可以看到 Ruby 实现始终回答相同的问题,即使结果一开始可能不直观。

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