为什么(a%256)与(a&0xFF)不同?

我总是假设在做(a % 256) ,优化器自然会使用有效的按位操作,就好像我写了(a & 0xFF)

在编译器浏览器gcc-6.2(-O3)上testing时:

 // 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 

好像我完全错过了一些东西。 有任何想法吗?

这是不一样的。 试试num = -79 ,你将得到两个操作的不同结果。 (-79) % 256 = -79 ,而(-79) & 0xff是一些正数。

使用unsigned int ,操作是相同的,代码将可能是相同的。

PS-有人评论说

它们不应该是相同的, a % b被定义为a - b * floor (a / b)

这不是在C,C ++,Objective-C(即问题中的代码将编译的所有语言)中定义的方式。

简短的回答

-1 % 256产生-1而不是255 ,这是-1 & 0xFF 。 因此,优化将是不正确的。

长答案

C ++的惯例是(a/b)*b + a%b == a ,这看起来很自然。 a/b总是返回没有小数部分的算术结果(截断为0)。 因此, a%b具有与a%b相同的符号或为0。

为了满足上述条件( (-1%256)*256 + -1%256 == -1 ),- -1/256产生0 ,因此-1%256必须是-1 。 这与-1&0xFF0xFF )明显不同。 因此,编译器无法优化你想要的方式。

N4606中的C ++标准[expr.mul§4]中的相关部分规定:

对于积分操作数,运算符产生的代数商与任何小数部分丢弃; 如果商a/b在结果types中可表示,则(a/b)*b + a%b等于a […]。

启用优化

但是, 使用unsignedtypes,优化将是完全正确的 ,满足上述约定:

 unsigned(-1)%256 == 0xFF 

另见这个 。

其他语言

这可以在不同的编程语言中处理得非常不同,你可以在Wikipedia上查看。

由于C ++ 11,如果num是负数, num % 256必须是非正值。

所以位模式将取决于系统上签名types的实现:对于负的第一个参数,结果不是提取最低有效的8位。

如果你的例子中的numunsigned这将是一个不同的问题:这些天我几乎希望编译器做出你所引用的优化。

我对编译器的推理没有心灵感应的洞察力,但是在%的情况下,需要处理负值(以及将分数舍入到零),而结果总是低8位。

sar指令对我来说听起来像是“移位算术右移”,用符号位值填充空位。

从math上讲,模数定义如下:

a%b = a – b * floor(a / b)

这个在这里应该为你清理它。 因为整数除法相当于floor(a / b),我们可以去掉整数。 但是,如果编译器使用像你所build议的一般技巧,它将需要为所有a和所有b工作。 不幸的是,事实并非如此。 从mathangular度来说,你的技巧是100%正确的无符号整数(我看到一个答案状态有符号整数中断,但我可以确认也不否认这 – -a%b应该是正数)。 但是,你可以为所有b做这个把戏吗? 可能不会。 这就是为什么编译器不这样做。 毕竟,如果模数很容易写成一个按位运算,那么我们只需添加一个模数电路,如加法和其他操作。