C++11中的取模运算符改变了吗?

13
在C++ 98/03中,可能会有重复的问题:C++ operator % guarantees

5.6-4

二元运算符/产生商,而二元%运算符产生第一个表达式除以第二个表达式的余数。如果/或%的第二个操作数为零,则行为未定义;否则(a/b)* b + a%b等于a。如果两个操作数都是非负的,则余数为非负的;如果不是,则余数的符号是实现定义的。 在c ++ 11中: 5.6 -4 二进制/运算符产生商,而二元%运算符产生第一个表达式除以第二个表达式的余数。如果/或%的第二个操作数为零,则行为未定义。对于整数操作数,/运算符产生代数商,任何小数部分都被丢弃;如果商a / b可以用结果类型表示,则(a / b)* b + a%b等于a。 正如您所看到的,符号位的实现定义缺失,它会发生什么?

1
@AnandVeeramani:有些人只是想避免未定义(或在这种情况下,实现定义)的行为。我很高兴他问了这个问题,因为当值可能为负数时,我已经避免使用模运算。 - moswald
1
另外,请参见:https://dev59.com/4HA65IYBdhLWcg3w1SRC - Mankarse
1
C++ 98/03还有这样的脚注:“根据正在进行的修订ISO C工作,整数除法的首选算法遵循ISO Fortran标准(ISO/IEC 1539:1991)中定义的规则,其中商始终向零舍入。” C++11只是将其作为标准的要求(与C99对C所做的要求相同)。去除“%”运算符的实现定义部分是其结果。 - Michael Burr
@moswald:在这些情况下,您也必须避免使用“/”运算符,因为它同样是实现定义的。 - Michael Burr
正如答案中所提到的,C++11中整数操作数的/运算符行为要求向零截断。这在C99中也是正确的。之前的标准将其留给实现定义。请注意,div()函数(及其相关函数)始终被指定为向零截断。 - Michael Burr
显示剩余4条评论
2个回答

22

%在C++11中的行为已被加强,并且现在已完全规定(除了对0进行除法)。

截向零的组合和恒等式(a/b)*b + a%b == a意味着,对于正数aa%b始终为正,而对于负数aa%b则为负。


这种情况的数学原因如下:

÷为数学除法,/为C++除法。

对于任何ab,我们有a÷b = a/b + f(其中f为小数部分),根据标准,我们还有(a/b)*b + a%b == a

由于a/b0截断,因此如果a÷b为正,则小数部分始终为正,如果a÷b为负,则小数部分为负:

sign(f) == sign(a)*sign(b)

可以将a/b = a÷b - f重新排列。可以将a扩展为(a÷b)*b

(a/b)*b + a%b == a => (a÷b - f)*b+a%b == (a÷b)*b

现在可以将左侧进行扩展:

(a÷b)*b - f*b + a%b == (a÷b)*b

a%b == f*b

从前面回忆一下,sign(f)==sign(a)*sign(b),因此:

sign(a%b) == sign(f*b) == sign(a)*sign(b)*sign(b) == sign(a)


@mata:符号仅由a确定,您在算术运算中搞砸了(-1/2)= -0.5 = 0(截断后),因此0 + x = -1,所以x = -1。 - Mankarse
@Mankarse:这个方程式一直存在(就像03标准中一样),唯一新的东西似乎是所谓的“朝零截尾”。这个“朝零截尾”是什么意思? - RoundPi
1
@Gob00st:向零截断意味着在结果中舍弃除法结果的小数部分,因此5/4 == 1.25 => 1(舍弃0.25),而-7/4 == -1.75 => -1(舍弃-0.75)。 - Mankarse
我在哪里可以找到有关C++11的更改信息,例如模数运算符%的行为?我发现的有关C++ 11新功能的网站没有提到模数运算符的更改。我需要从ISO购买C++标准吗?这些关于C ++ 的信息不是免费提供的吗? - Jorge Luque
1
@JorgeLuque:从技术上讲,您需要从ISO或国家标准机构之一购买C++标准,但标准的最终草案是免费提供的,并且通常非常类似于官方标准。请参见isocpp.org - Mankarse
显示剩余4条评论

3
算法表明 (a/b)*b + a%b = a,如果你记得它是 truncate(a/b)*b + a%b = a 更容易理解。使用代数,a%b = a - truncate(a/b)*b。也就是说,f(a,b) = a - truncate(a/b)*b。对于哪些值 f(a,b) < 0
无论 b 是正数还是负数都没有关系。它会被取消,因为它出现在分子和分母中。即使 truncate(a/b) = 0 并且 b 是负数,那么当它是 0 的乘积时,它也将被取消。
因此,只有 a 的符号决定了 f(a,b) 或者 a%b 的符号。

该方程式始终存在,就像03标准一样... - RoundPi
3
区别在于,在C++03中,除法取整没有完全指定。 - Mankarse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接