我很好奇这些语言(Java,C ...)忽略了模数运算的数学定义。
在模块操作中返回负值有什么意义(根据定义,它应该总是返回正数)?
答案 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的方式。
最初,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
成立。由于除法运算符被定义为截断为零,因此约束了余数的符号。