为什么(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&0xFF
( 0xFF
)明显不同。 因此,编译器无法优化你想要的方式。
N4606中的C ++标准[expr.mul§4]中的相关部分规定:
对于积分操作数,运算符产生的代数商与任何小数部分丢弃; 如果商
a/b
在结果types中可表示,则(a/b)*b + a%b
等于a
[…]。
启用优化
但是, 使用unsigned
types,优化将是完全正确的 ,满足上述约定:
unsigned(-1)%256 == 0xFF
另见这个 。
其他语言
这可以在不同的编程语言中处理得非常不同,你可以在Wikipedia上查看。
由于C ++ 11,如果num
是负数, num % 256
必须是非正值。
所以位模式将取决于系统上签名types的实现:对于负的第一个参数,结果不是提取最低有效的8位。
如果你的例子中的num
是unsigned
这将是一个不同的问题:这些天我几乎希望编译器做出你所引用的优化。
我对编译器的推理没有心灵感应的洞察力,但是在%
的情况下,需要处理负值(以及将分数舍入到零),而结果总是低8位。
sar
指令对我来说听起来像是“移位算术右移”,用符号位值填充空位。
从math上讲,模数定义如下:
a%b = a – b * floor(a / b)
这个在这里应该为你清理它。 因为整数除法相当于floor(a / b),我们可以去掉整数。 但是,如果编译器使用像你所build议的一般技巧,它将需要为所有a和所有b工作。 不幸的是,事实并非如此。 从mathangular度来说,你的技巧是100%正确的无符号整数(我看到一个答案状态有符号整数中断,但我可以确认也不否认这 – -a%b应该是正数)。 但是,你可以为所有b做这个把戏吗? 可能不会。 这就是为什么编译器不这样做。 毕竟,如果模数很容易写成一个按位运算,那么我们只需添加一个模数电路,如加法和其他操作。