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

Mar*_*oma 52 c++ standards modulo

数学:

如果你有这样的等式:

x = 3 mod 7
Run Code Online (Sandbox Code Playgroud)

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

x = 3 + k * 7
Run Code Online (Sandbox Code Playgroud)

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

Python:

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

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

m = 7

for i in xrange(-8, 10 + 1):
    print(i % 7)
Run Code Online (Sandbox Code Playgroud)

结果是:

6    0    1    2    3    4    5    6    0    1    2    3    4    5    6    0    1    2    3
Run Code Online (Sandbox Code Playgroud)

C++:

#include <iostream>

using namespace std;

int main(){
    int m = 7;

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

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

将输出:

-1    0    -6    -5    -4    -3    -2    -1    0    1    2    3    4    5    6    0    1    2    3    
Run Code Online (Sandbox Code Playgroud)

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.有人知道从哪里获取它吗?)

该操作似乎定义如下:

在此输入图像描述

问题:

这样定义它是否有意义?

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

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

附加材料:

有关modulo在不同语言中的作用,请参阅Wikipedia.

eca*_*mur 27

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

整数部门遵守两条规则:

  • 非整数商向零舍入; 和
  • dividend = quotient*divisor + remainder结果满足等式.

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

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

  • 处理器指令集设计针对不太常见的情况(模数)优化常见情况(除法);
  • 一致性(向零舍入,并且遵守除法方程)优于数学正确性;
  • C更喜欢效率和简单(特别是考虑到将C视为"高级汇编程序"的倾向); 和
  • C++更喜欢与C兼容.


fre*_*low 10

这个规范的论据是什么?

C++的设计目标之一是有效地映射到硬件.如果底层硬件以产生负余数的方式实现除法,那么如果你%在C++中使用它就会得到.这就是真的.

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

您将在comp.lang.c ++.moderated以及在较小程度上comp.lang.c ++上找到有趣的讨论

  • C++的目标是"你不为你不使用的东西买单",这非常适合.为方便起见,默认情况下不会牺牲性能.如果您需要检查/`abs`您的模数结果,您可以轻松地将其包装在您需要该行为的任何位置. (3认同)

Jiv*_*son 10

回到那一天,设计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; }
Run Code Online (Sandbox Code Playgroud)

你效率很高.

这些天我基本上使用了以下内容,其中包含了一些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;
}
Run Code Online (Sandbox Code Playgroud)

真实的事实:我游说帕斯卡标准委员会以正确的方式进行调整,直到他们妥协.令我恐惧的是,他们以错误的方式进行整数除法.所以他们甚至不匹配.

编辑:清洁工给了我一个主意.我正在研究一个新的.

#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);
    }
}
Run Code Online (Sandbox Code Playgroud)