【发布时间】:2011-11-27 12:19:59
【问题描述】:
为什么要这样操作:
std::cout << (-7 % 3) << std::endl;
std::cout << (7 % -3) << std::endl;
给出不同的结果?
-1
1
【问题讨论】:
标签: c++
为什么要这样操作:
std::cout << (-7 % 3) << std::endl;
std::cout << (7 % -3) << std::endl;
给出不同的结果?
-1
1
【问题讨论】:
标签: c++
在这种情况下(即当一个或两个操作数为负时)的 符号 是实现定义的。规范在 §5.6/4 (C++03) 中说,
二元 / 运算符产生商,二元 % 运算符产生第一个表达式除以第二个表达式的余数。如果 / 或 % 的第二个操作数为零,则行为未定义;否则 (a/b)*b + a%b 等于 a。如果两个操作数都是非负数,则余数是非负数; 如果不是,余数的符号是实现定义的。
就 C++03 而言,这就是语言要说的全部内容。
【讨论】:
-7/3 会导致-3,但那将是在非常遥远的过去。)
b 是 2 的幂,过去可以将 n % b 计算为 n & (b-1)。新标准要求将其计算为n < 0 ? n | -b : n & (b-1)。同样,n / b 不能写成简单的移位,即使在支持算术移位的硬件上也是如此;相反,在这样的系统上,像n/16 这样的表达式(如果n 是int32)必须写成n < 0 ? (n+15) >> 4 : n >> 4。可怕的标准,恕我直言。请注意,由于非二次方除法本来就很慢,因此强制欧几里得行为不会减慢多少。
foo /= 16 可以写成asr [dword foo],4。在新规则下,最佳表示需要更多的指令 [也许mov eax,[foo] / mov ebx,eax, asr eax,31 / lsr eax,28 / add eax,ebx / asr eax,4 / mov [foo],eax] 不像除法指令那么慢,但比简单的移位要多得多。
来自 ISO14882:2011(e) 5.6-4:
二元 / 运算符产生商,二元 % 运算符 产生第一个表达式除以的余数 第二。如果 / 或 % 的第二个操作数为零,则行为是 不明确的。对于整数操作数, / 运算符产生代数商,并丢弃任何小数部分;如果商 a/b 是 可以用结果的类型来表示,(a/b)*b + a%b 等于 a。
剩下的就是基础数学:
(-7/3) => -2
-2 * 3 => -6
so a%b => -1
(7/-3) => -2
-2 * -3 => 6
so a%b => 1
注意
如果两个操作数都是非负数,则余数是非负数;如果 不是,余数的符号是实现定义的。
来自 ISO14882:2003(e) 的 ISO14882:2011(e) 中不再存在
【讨论】:
-7/3 的结果是-2 还是-3)。
% 的这个问题。
a % b
在 C++ 中默认:
(-7/3) => -2
-2 * 3 => -6
so a%b => -1
(7/-3) => -2
-2 * -3 => 6
so a%b => 1
在python中:
-7 % 3 => 2
7 % -3 => -2
在 c++ 中到 python:
(b + (a%b)) % b
【讨论】: