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

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

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

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

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

 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)并翻转符号if (A < 0) xor (B < 0)

如果余数非零,则地板除法具有从商中减去1的附加步骤。

至少在Java中,它不是模数运算符 – 它是余数运算符 。

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

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

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

来自维基百科 (我的重点):

给定两个正数,a(被除数)和n(除数),模n(缩写为mod n)可以被认为是n除以a的余数。 例如,表达式“5 mod 4”将评估为1,因为5除以4会留下1的余数,而“9 mod 3”将评估为0,因为9除以3会使余数为0; 在乘以3次3后,没有什么可以从9中减去。(请注意,使用计算器进行除法不会显示此操作引用的结果,商将表示为小数。) 当a或者n是负数,这个天真的定义分解,编程语言在定义这些值方面有所不同。 尽管通常在a和n都是整数的情况下执行,但许多计算系统允许其他类型的数字操作数。 n的整数模数的范围是0到n-1。(n mod 1总是0; n mod 0未定义,可能导致计算机编程语言中的“除以零”错误)参见模拟算术数论中应用的较旧且相关的惯例。

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

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

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

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

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

在C或Java标准中, %称为模数运算符 – 而是返回余数

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