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

时间:2016-11-08 09:29:02

标签: c++ optimization

我总是假设在执行(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

好像我完全错过了什么。 有什么想法吗?

5 个答案:

答案 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%ba具有相同的符号或为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位。

如果你的案例numunsigned,那将是另一回事:这些天我几乎期望一个编译器来进行你引用的优化。< / 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很容易写成一个按位运算,那么我们只需添加一个模数电路,如加法和其他操作。