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

我一直认为,在编写 (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

看来我完全遗漏了什么。 有什么想法吗?

16991 次浏览

因为 C + + 11,如果 num是负的,num % 256必须是非正的。

因此,位模式将取决于系统上有符号类型的实现: 对于负的第一个参数,结果不是提取最低有效的8位。

如果你的 numunsigned,那就是另外一回事了: 这些天来,我几乎把 期待当作一个编译器来进行你所引用的优化。

我对编译器的推理没有心灵感应的洞察力,但是在 %的情况下,有必要处理负值(除法圆向零) ,而在 &的情况下,结果总是较低的8位。

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

这不一样。尝试 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%ba具有相同的符号或者是0。

-1/256产生 0,因此 -1%256必须是 -1,以满足上述条件((-1%256)*256 + -1%256 == -1)。这明显不同于 -1&0xFF也就是 0xFF。因此,编译器不能按照您希望的方式进行优化。

C + + 标准[ expr.mul 4]作为 N4606的相关章节指出:

对于整数操作数,/算子产生丢弃任何小数部分的代数商; 如果商 a/b在结果类型中是可表示的,则 (a/b)*b + a%b等于 a[ ... ]。

启用优化

然而,满足上述约定的 使用 unsigned类型,优化将是完全正确的:

unsigned(-1)%256 == 0xFF

参见 这个

其他语言

在不同的编程语言之间处理这个问题的方式非常不同,因为您可以查看 维基百科