某些语言是否允许负模数?

时间:2011-11-29 22:42:22

标签: java c math modulus

我很好奇这些语言(Java,C ...)忽略了模数运算的数学定义。

在模块操作中返回负值有什么意义(根据定义,它应该总是返回正数)?

6 个答案:

答案 0 :(得分:7)

至少在Java中,它不是模数运算符 - 它是remainder operator

我认为选择这种方式的原因是为了使这种关系起作用(来自JLS):

  

二进制数字提升(第5.6.2节)后整数操作数的余数运算产生的结果值使得(a / b)* b +(a%b)等于a。即使在特殊情况下,这种同一性仍然存在,即被除数是其类型的最大可能量值的负整数,且除数为-1(余数为0)。根据这个规则可以得出,只有当被除数为负时,余数运算的结果才是负数,只有当被除数为正时才能为正;而且,结果的大小总是小于除数的大小。

作为定义的一部分,这种平等关系似乎是合理的。如果你将除法截断为零作为给定,则会给你一个负余数。

答案 1 :(得分:6)

来自Wikipedia(我的重点):

  

给出两个正数,a(被除数)和n(除数),a   modulo n(缩写为mod n)可以被认为是余数,   关于a的划分例如,表达“5 mod 4”会   评估为1,因为5除以4留下余数为1,而“9   mod 3“将评估为0,因为9乘以3除以a   余数为0;乘法后没有什么可以减去9   3次3.(注意用计算器进行除法不会   显示此操作引用的结果,商   将表示为小数。)当a或n为负数时,   这个天真的定义打破了,编程语言也有所不同   如何定义这些值。虽然通常使用   并且n都是整数,许多计算系统允许其他类型的   数字操作数。整数模n的数字范围是0   到n - 1.(n mod 1总是0; n mod 0可能是未定义的   导致计算机编程中出现“除零”错误   语言)参见旧算法和相关约定的模运算   应用于数论。

答案 2 :(得分:4)

我怀疑余数运算符是故意设计为具有那些语义,我同意这些语义不是很有用。 (你会写一个日历程序,显示工作日的星期日,反星期六,反星期五,......,反星期一,以便在纪元之前的日期?)

相反,负余数是定义整数除法的副作用。

A rem B := A - (A div B) * B

如果将A div B定义为trunc(A/B),则会获得C %运算符。如果将A div B定义为floor(A/B),则会获得Python的%运算符。其他定义也是可能的。

所以,真正的问题是:

为什么C ++,Java,C#等使用截断整数除法?

因为这就是C的方式。

为什么C使用截断除法?

最初,C没有说明/应如何处理负数。它把它留给了硬件。

实际上,每个重要的C实现都使用截断除法,所以在1999年这些语义正式成为C标准的一部分。

为什么硬件使用截断分区?

因为在无符号除法方面实现起来更容易(=更便宜)。您只需计算abs(A) div abs(B)并翻转符号(A < 0) xor (B < 0)

如果余数为非零,则地板除法有额外的步骤从商中减去1。

答案 3 :(得分:3)

大多数未定义为返回模数。他们被定义为返回的是剩余部分,正负值同样合理。

在C或C ++中,说它应该产生底层硬件产生的任何东西是非常合理的。这个借口/原因对Java来说几乎没有效果。

另请注意,在C89 / 90和C ++ 98/03中,剩余部分可以是正数或负数,只要余数和除法的结果一起工作((a/b)*b+a%b == a)。在C99和C ++ 11中,规则已经收紧,因此除法必须截断为零,如果有余数则必须为负数。

答案 4 :(得分:1)

返回模数负值的一个实用理由是实现模数的硬件指令会这样做。

因此,标准会使定义不明确,因此编译器可以做任何更简单的事情。

答案 5 :(得分:1)

在{C}或Java标准中,%都不是模数运算符 - 而是返回余数

定义为返回否定红利的负数,以便只要(a/b)*b + a%b == a可表示关系a / b成立。由于除法运算符被定义为截断为零,因此约束了余数的符号。