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

数学

如果你有这样的等式:

x = 3 mod 7

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

x = 3 + k * 7

其中k可以是任何整数。 我不知道模数运算是为数学定义的,但是这个因素肯定是。

Python

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

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

m = 7

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

结果是:

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

C ++:

#include <iostream>

using namespace std;

int main(){
    int m = 7;

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

    return 0;
}

会输出:

-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乘法运算符:

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

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

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

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

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

问题

这样定义它有意义吗?

这个规范的参数是什么? 有没有一个地方制定这样的标准的人讨论它呢? 在哪里我可以读到一些关于他们为什么决定这样做的原因?

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

其他材料

请参阅维基百科,了解模数在不同语言中的长列表。


在x86(以及其他处理器体系结构)上,整数除法和模数通过一个单独的操作idivdiv for unsigned values)来执行,它产生商和余数(对于字大小的参数,分别在AXDX )。 这用在C库函数divmod ,可以通过编译器将其优化为单条指令!

整数部分尊重两个规则:

  • 非整数商数四舍五入到零; 和
  • 等式dividend = quotient*divisor + remainder由结果满足。
  • 因此,当将负数除以正数时,商将是负数(或零)。

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

  • 处理器指令集设计针对常见情况(模块)上的常见情况(部分)进行优化;
  • 一致性(舍入为零,并尊重分割公式)优于数学正确性;
  • C更喜欢效率和简单(特别是考虑到将C视为“高级汇编人员”的倾向); 和
  • C ++更喜欢与C兼容。

  • 这个规范的参数是什么?

    C ++的设计目标之一是有效地映射到硬件。 如果底层硬件以产生负余量的方式实现分区,那么如果您在C ++中使用% ,那么就是这样。 这就是它的全部。

    有没有一个地方制定这样的标准的人讨论它呢?

    你可以在comp.lang.c ++。moderated中找到有趣的讨论,在较小​​的程度上可以找到comp.lang.c ++


    当天,设计x86指令集的人决定将整数除法舍入到零而不是舍去是正确的。 (可能一千只骆驼的跳蚤嵌入母亲的胡须中。)为了保持一些数学正确性的外观,操作符REM被发音为“剩余”,必须相应地表现出来。 不要阅读以下内容:https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzatk/REM.htm

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

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

    有你的效率。

    这些天我基本上使用了下面的内容,并引入了一些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;
    }
    

    事实是:我游说帕斯卡标准委员会做出正确的方式,直到他们改变态度。 令我惊骇的是,他们以错误的方式进行了整数除法。 所以他们甚至不匹配。

    编辑:清晰的给了我一个主意。 我正在研究一个新的。

    #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);
        }
    }
    
    链接地址: http://www.djcxy.com/p/20793.html

    上一篇: Why does C++ output negative numbers when using modulo?

    下一篇: Resource that briefly describe the C and C++ standards