某些语言允许负模数是否有原因?
我对这些忽略模数运算的数学定义的语言(Java、C ...)感到好奇。
在模块操作中返回负值有什么意义(根据定义,应该始终返回正数)?
I am curious about these languages (Java, C ...) which ignore mathematical definition of modulus operation.
What is the point of returning negative values in a module operation (that, by definition, should allways return a positive number)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
来自维基百科(我的重点):
From Wikipedia (my emphasis):
它们中的大多数都没有定义为返回模数。它们定义返回的是余数,正值和负值同样合理。
在 C 或 C++ 中,可以很合理地说它应该生成底层硬件生成的任何内容。但这个借口/理由对于 Java 来说几乎不起作用。
另请注意,在 C89/90 和 C++98/03 中,余数可以是正数也可以是负数,只要余数和除法的结果一起工作即可 (
(a/b)*b+a%b == a
)。在 C99 和 C++11 中,规则更加严格,因此除法必须截断为零,如果有余数,则必须为负数。Most of them are not defined to return a modulus. What they're defined to return is a remainder, for which positive and negative values are equally reasonable.
In C or C++ it's pretty reasonable to say it should produce whatever the underlying hardware produces. That excuse/reason doesn't work nearly as well for Java though.
Also note that in C89/90 and C++98/03, the remainder could be either positive or negative, as long as the results from remainder and division worked together (
(a/b)*b+a%b == a
). In C99 and C++11, the rules been tightened up so the division must truncate toward zero, and if there is a remainder it must be negative.返回模数负值的一个务实原因是实现模数的硬件指令会这样做。
因此,标准对其进行了错误的定义,以便编译器可以做任何对他们来说更简单的事情。
A pragmatic reason to return a negative value for modulus would be that the hardware instruction implementing modulus does so.
So standards leave that ill defined, so that compilers can do whatever is simpler to them.
在 C 或 Java 标准中,
%
都不被称为模运算符 - 相反,它返回余数。它被定义为负股息返回负数,因此关系
(a/b)*b + a%b == a
成立,只要a / b
是有代表性的。由于除法运算符被定义为向零截断,因此这限制了余数的符号。In neither of the C or Java standards is
%
referred to as a modulus operator - rather, it returns the remainder.It is defined to return negative numbers for negative dividends so that the relation
(a/b)*b + a%b == a
holds, as long asa / b
is representable. Since the division operator is defined to truncate towards zero, this constrains the sign of the remainder.至少在 Java 中,它不是模数运算符 - 它是
我相信选择这种方式的原因是为了使这种关系发挥作用(来自 JLS):
这种平等关系似乎可以作为定义的一部分使用。如果您将除法向零截断作为给定,那么您将得到负余数。
In Java at least, it's not a modulus operator - it's a remainder operator.
I believe the reason for it being chosen that way is to make this relation work (from the JLS):
That equality relation seems like a reasonable thing to use as part of the definition. If you take division truncating towards zero to be a given, that leaves you with a negative remainder.
我怀疑余数运算符是故意设计为具有这些语义的,我同意这不是很有用。 (您是否会编写一个日历程序来显示纪元之前的日期的周日、反周六、反周五、……、反周一?)
相反,负余数是整数除法的副作用定义的。
如果
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。
I doubt that the remainder operator was deliberately designed to have those semantics, which I agree aren't very useful. (Would you ever write a calendar program that shows the weekdays Sunday, Anti-Saturday, Anti-Friday, ..., Anti-Monday for dates before the epoch?)
Rather, negative remainders are a side effect of the way integer division is defined.
If
A div B
is defined astrunc(A/B)
, you get C's%
operator. IfA div B
is defined asfloor(A/B)
, you get Python's%
operator. Other definitions are possible.So, the real question is:
Why do C++, Java, C#, etc. use truncating integer division?
Because that's the way that C does it.
Why does C use truncating division?
Originally, C didn't specify how
/
should handle negative numbers. It left it up to the hardware.In practice, every significant C implementation used truncating division, so in 1999 these semantics were formally made a part of the C standard.
Why does hardware use truncating division?
Because it's easier (=cheaper) to implement in terms of unsigned division. You just calculate
abs(A) div abs(B)
and flip the sign if(A < 0) xor (B < 0)
.Floored division has the additional step of subtracting 1 from the quotient if the remainder is nonzero.