C++ 为什么C++在使用模时输出负数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11630321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:22:23  来源:igfitidea点击:

Why does C++ output negative numbers when using modulo?

c++standardsmodulo

提问by Martin Thoma

Math:

数学

If you have an equation like this:

如果你有一个这样的方程:

x = 3 mod 7

x could be ... -4, 3, 10, 17, ..., or more generally:

x 可以是 ... -4, 3, 10, 17, ...,或更一般地说:

x = 3 + k * 7

where k can be any integer. I don't know of a modulo operation is defined for math, but the factor ring certainly is.

其中 k 可以是任何整数。我不知道为数学定义了模运算,但因子环肯定是。

Python:

蟒蛇

In Python, you will always get non-negative values when you use %with a positive m:

在 Python 中,当您使用%正数时,您将始终获得非负值m

#!/usr/bin/python
# -*- coding: utf-8 -*-

m = 7

for i in xrange(-8, 10 + 1):
    print(i % 7)

Results in:

结果是:

6    0    1    2    3    4    5    6    0    1    2    3    4    5    6    0    1    2    3

C++:

C++:

#include <iostream>

using namespace std;

int main(){
    int m = 7;

    for(int i=-8; i <= 10; i++) {
        cout << (i % m) << endl;
    }

    return 0;
}

Will output:

将输出:

-1    0    -6    -5    -4    -3    -2    -1    0    1    2    3    4    5    6    0    1    2    3    

ISO/IEC 14882:2003(E) - 5.6 Multiplicative operators:

ISO/IEC 14882:2003(E) - 5.6 乘法运算符:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a. If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined 74).

二元 / 运算符产生商,二元 % 运算符产生第一个表达式除以第二个表达式的余数。如果 / 或 % 的第二个操作数为零,则行为未定义;否则 (a/b)*b + a%b 等于 a。如果两个操作数都为非负,则余数为非负;如果不是,则余数的符号是​​实现定义的 74)

and

74) According to work underway toward the revision of ISO C, the preferred algorithm for integer division follows the rules defined in the ISO Fortran standard, ISO/IEC 1539:1991, in which the quotient is always rounded toward zero.

74) 根据正在进行的 ISO C 修订工作,整数除法的首选算法遵循 ISO Fortran 标准 ISO/IEC 1539:1991 中定义的规则,其中商总是向零舍入。

Source: ISO/IEC 14882:2003(E)

资料来源:ISO/IEC 14882:2003(E)

(I couldn't find a free version of ISO/IEC 1539:1991. Does anybody know where to get it from?)

(我找不到免费版本的ISO/IEC 1539:1991。有人知道从哪里得到它吗?)

The operation seems to be defined like this:

该操作似乎是这样定义的:

enter image description here

在此处输入图片说明

Question:

问题

Does it make sense to define it like that?

这样定义有意义吗?

What are arguments for this specification? Is there a place where the people who create such standards discuss about it? Where I can read something about the reasons why they decided to make it this way?

这个规范的论据是什么?是否有创建此类标准的人讨论它的地方?我在哪里可以阅读有关他们决定这样做的原因的信息?

Most of the time when I use modulo, I want to access elements of a datastructure. In this case, I have to make sure that mod returns a non-negative value. So, for this case, it would be good of mod always returned a non-negative value. (Another usage is the Euclidean algorithm. As you could make both numbers positive before using this algorithm, the sign of modulo would matter.)

大多数情况下,当我使用模数时,我想访问数据结构的元素。在这种情况下,我必须确保 mod 返回一个非负值。因此,对于这种情况,最好 mod 总是返回一个非负值。(另一种用法是欧几里得算法。因为您可以在使用此算法之前使两个数字均为正数,所以模的符号很重要。)

Additional material:

附加材料

See Wikipediafor a long list of what modulo does in different languages.

有关modulo 在不同语言中的作用的详细列表,请参阅Wikipedia

采纳答案by ecatmur

On x86 (and other processor architectures), integer division and modulo are carried out by a single operation, idiv(divfor unsigned values), which produces both quotient and remainder (for word-sized arguments, in AXand DXrespectively). This is used in the C library function divmod, which can be optimised by the compiler to a single instruction!

在 x86(和其他处理器体系结构)上,整数除法和取模由单个操作idivdiv对于无符号值)执行,它产生商和余数(分别对于字大小的参数,inAXDX)。这是在C库函数中使用的divmod,可以被编译器优化为单条指令!

Integer division respects two rules:

整数除法遵守两条规则:

  • Non-integer quotients are rounded towards zero; and
  • the equation dividend = quotient*divisor + remainderis satisfied by the results.
  • 非整数商向零四舍五入;和
  • dividend = quotient*divisor + remainder结果满足方程。

Accordingly, when dividing a negative number by a positive number, the quotient will be negative (or zero).

因此,当负数除以正数时,商将为负(或零)。

So this behaviour can be seen as the result of a chain of local decisions:

所以这种行为可以看作是一系列本地决策的结果:

  • Processor instruction set design optimises for the common case (division) over the less common case (modulo);
  • Consistency (rounding towards zero, and respecting the division equation) is preferred over mathematical correctness;
  • C prefers efficiency and simplicitly (especially given the tendency to view C as a "high level assembler"); and
  • C++ prefers compatibility with C.
  • 处理器指令集设计针对较不常见的情况(模)优化了常见情况(除法);
  • 一致性(向零舍入,并遵守除法方程)优于数学正确性;
  • C 更喜欢效率和简单(特别是考虑到将 C 视为“高级汇编程序”的趋势);和
  • C++ 更喜欢与 C 兼容。

回答by Jive Dadson

Back in the day, someone designing the x86 instruction set decided it was right and good to round integer division toward zero rather than round down. (May the fleas of a thousand camels nest in his mother's beard.) To keep some semblance of math-correctness, operator REM, which is pronounced "remainder", had to behave accordingly. DO NOT read this: https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzatk/REM.htm

回到当天,设计 x86 指令集的人认为将整数除法向零舍入而不是向下舍入是正确且好的。(愿一千只骆驼的跳蚤在他母亲的胡须中筑巢。)为了保持一些数学正确性,运算符 REM(发音为“剩余”)必须相应地进行操作。请勿阅读:https: //www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzatk/REM.htm

I warned you. Later someone doing the C spec decided it would be conforming for a compiler to do it either the right way or the x86 way. Then a committee doing the C++ spec decided to do it the C way. Then later yet, after this question was posted, a C++ committee decided to standardize on the wrong way. Now we are stuck with it. Many a programmer has written the following function or something like it. I have probably done it at least a dozen times.

我警告过你。后来有人做 C 规范决定编译器以正确的方式或 x86 方式来做这件事是符合要求的。然后一个制定 C++ 规范的委员会决定用 C 的方式来做。后来,在发布这个问题之后,C++ 委员会决定以错误的方式进行标准化。现在我们被它困住了。许多程序员编写了以下函数或类似的东西。我可能至少做了十几次。

 inline int mod(int a, int b) {int ret = a%b; return ret>=0? ret: ret+b; }

There goes your efficiency.

这就是你的效率。

These days I use essentially the following, with some type_traits stuff thrown in. (Thanks to Clearer for a comment that gave me an idea for an improvement using latter day C++. See below.)

这些天我基本上使用以下内容,并加入了一些 type_traits 的东西。(感谢 Clearer 的评论,让我有了使用后期 C++ 进行改进的想法。见下文。)

<strike>template<class T>
inline T mod(T a, T b) {
    assert(b > 0);
    T ret = a%b;
    return (ret>=0)?(ret):(ret+b);
}</strike>

template<>
inline unsigned mod(unsigned a, unsigned b) {
    assert(b > 0);
    return a % b;
}

True fact: I lobbied the Pascal standards committee to do mod the right way until they relented. To my horror, they did integer division the wrong way. So they do not even match.

真实事实:我游说 Pascal 标准委员会以正确的方式进行修改,直到他们心软。令我震惊的是,他们以错误的方式进行了整数除法。所以他们甚至不匹配。

EDIT: Clearer gave me an idea. I am working on a new one.

编辑:Clearer 给了我一个想法。我正在研究一个新的。

#include <type_traits>

template<class T1, class T2>
inline T1 mod(T1 a, T2 b) {
    assert(b > 0);
    T1 ret = a % b;
    if constexpr  ( std::is_unsigned_v<T1>)
    {
        return ret;
    } else {
        return (ret >= 0) ? (ret) : (ret + b);
    }
}

回答by fredoverflow

What are arguments for this specification?

这个规范的论据是什么?

One of the design goals of C++ is to map efficiently to hardware. If the underlying hardware implements division in a way that produces negative remainders, then that's what you'll get if you use %in C++. That's all there is to it really.

C++ 的设计目标之一是高效地映射到硬件。如果底层硬件以产生负余数的方式实现除法,那么这就是%在 C++ 中使用的结果。这就是全部。

Is there a place where the people who create such standards discuss about it?

是否有创建此类标准的人讨论它的地方?

You will find interesting discussions on comp.lang.c++.moderated and, to a lesser extent, comp.lang.c++

您会发现关于 comp.lang.c++.moderated 以及在较小程度上的 comp.lang.c++ 的有趣讨论