我总是假设在执行(a % 256)
时,优化器自然会使用有效的按位运算,就好像我写了(a & 0xFF)
一样。
在编译器资源管理器gcc-6.2(-O3)上进行测试时:
// Type your code here, or load an example.
int mod(int num) {
return num % 256;
}
mod(int):
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
ret
在尝试其他代码时:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
好像我完全错过了什么。 有什么想法吗?
答案 0 :(得分:228)
不一样。试试num = -79
,您将从这两项操作中获得不同的结果。 (-79) % 256 = -79
,而(-79) & 0xff
是一些正数。
使用unsigned int
,操作相同,代码可能相同。
PS - 有人发表了评论
它们不应相同,
a % b
定义为a - b * floor (a / b)
。
这不是它在C,C ++,Objective-C中定义的方式(即问题中的代码将编译的所有语言)。
答案 1 :(得分:54)
-1 % 256
产生-1
而非255
产生-1 & 0xFF
。因此,优化将是不正确的。
C ++的约定是(a/b)*b + a%b == a
,这似乎很自然。 a/b
总是返回没有小数部分的算术结果(截断为0)。因此,a%b
与a
具有相同的符号或为0.
分部-1/256
会产生0
,因此-1%256
必须为-1
才能满足上述条件((-1%256)*256 + -1%256 == -1
)。这明显不同于-1&0xFF
0xFF
。因此,编译器无法优化您想要的方式。
C++ standard [expr.mul §4] as of N4606州的相关部分:
对于积分操作数,
/
运算符产生代数商,丢弃任何小数部分;如果商a/b
可在结果类型中表示,则(a/b)*b + a%b
等于a
[...]。
但是,使用unsigned
类型,优化将完全正确,满足上述惯例:
unsigned(-1)%256 == 0xFF
另见this。
这可以在不同的编程语言中处理得非常不同,因为您可以查看Wikipedia。
答案 2 :(得分:50)
自C ++ 11以来,如果num % 256
为负数,则num
必须为非正数。
因此位模式将取决于系统上已签名类型的实现:对于负的第一个参数,结果不是提取最低有效8位。
如果你的案例num
是unsigned
,那将是另一回事:这些天我几乎期望一个编译器来进行你引用的优化。< / p>
答案 3 :(得分:11)
我没有对编译器的推理有远见的洞察力,但在%
的情况下,有必要处理负值(并将分数舍入为零),而&
结果总是低8位。
sar
指令听起来像#34;右移算术&#34;,用符号位值填充空出的位。
答案 4 :(得分:0)
从数学上讲,modulo定义如下:
a%b = a - b * floor(a / b)
这里的内容应该为您清除。我们可以消除整数的下限,因为整数除法等于floor(a / b)。但是,如果编译器使用像你建议的一般技巧,它将需要适用于所有a和所有b。不幸的是,事实并非如此。从数学上讲,你的诀窍是无符号整数100%正确(我看到答案状态有符号整数中断,但我可以确认也不否认这一点,因为-a%b应该是正数)。但是,你能为所有b做这个伎俩吗?可能不是。这就是编译器不这样做的原因。毕竟,如果modulo很容易写成一个按位运算,那么我们只需添加一个模数电路,如加法和其他操作。