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

发布于 2024-12-18 11:02:07 字数 82 浏览 4 评论 0原文

我对这些忽略模数运算的数学定义的语言(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无名指的心愿 2024-12-25 11:02:08

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

给定两个正数,a(被除数)和 n(除数),a
modulo n(缩写为 mod n)可以被认为是余数,
a 除以 n。例如,表达式“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 未定义,可能
导致计算机编程中出现“除以零”错误
语言)请参阅模算术以了解较旧的相关约定
应用于数论。

From Wikipedia (my emphasis):

Given two positive numbers, a (the dividend) and n (the divisor), a
modulo n (abbreviated as a mod n) can be thought of as the remainder,
on division of a by n. For instance, the expression "5 mod 4" would
evaluate to 1 because 5 divided by 4 leaves a remainder of 1, while "9
mod 3" would evaluate to 0 because the division of 9 by 3 leaves a
remainder of 0; there is nothing to subtract from 9 after multiplying
3 times 3. (Notice that doing the division with a calculator won't
show you the result referred to here by this operation, the quotient
will be expressed as a decimal.) When either a or n is negative,
this naive definition breaks down and programming languages differ in
how these values are defined.
Although typically performed with a
and n both being integers, many computing systems allow other types of
numeric operands. The range of numbers for an integer modulo of n is 0
to n - 1. (n mod 1 is always 0; n mod 0 is undefined, possibly
resulting in a "Division by zero" error in computer programming
languages) See modular arithmetic for an older and related convention
applied in number theory.

旧话新听 2024-12-25 11:02:08

它们中的大多数都没有定义为返回模数。它们定义返回的是余数,正值和负值同样合理。

在 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.

也只是曾经 2024-12-25 11:02:08

返回模数负值的一个务实原因是实现模数的硬件指令会这样做。

因此,标准对其进行了错误的定义,以便编译器可以做任何对他们来说更简单的事情。

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.

晨敛清荷 2024-12-25 11:02:08

在 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 as a / b is representable. Since the division operator is defined to truncate towards zero, this constrains the sign of the remainder.

给我一枪 2024-12-25 11:02:07

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

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

二进制数字提升后的整数操作数的余数运算(第 5.6.2 节)生成的结果值使得 (a/b)*b+(a%b) 等于 a。即使在被除数是其类型的最大可能的负整数且除数为 -1(余数为 0)的特殊情况下,该恒等式也成立。由该规则可知,只有被除数为负时,余数运算的结果才能为负;只有被除数为正时,余数运算的结果才能为正;而且,结果的大小总是小于除数的大小。

这种平等关系似乎可以作为定义的一部分使用。如果您将除法向零截断作为给定,那么您将得到负余数。

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):

The remainder operation for operands that are integers after binary numeric promotion (§5.6.2) produces a result value such that (a/b)*b+(a%b) is equal to a. This identity holds even in the special case that the dividend is the negative integer of largest possible magnitude for its type and the divisor is -1 (the remainder is 0). It follows from this rule that the result of the remainder operation can be negative only if the dividend is negative, and can be positive only if the dividend is positive; moreover, the magnitude of the result is always less than the magnitude of the divisor.

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.

心房的律动 2024-12-25 11:02:07

我怀疑余数运算符是故意设计为具有这些语义的,我同意这不是很有用。 (您是否会编写一个日历程序来显示纪元之前的日期的周日、反周六、反周五、……、反周一?)

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

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。

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.

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

If A div B is defined as trunc(A/B), you get C's % operator. If A div B is defined as floor(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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文