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

Ela*_*iss 143 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
Run Code Online (Sandbox Code Playgroud)

在尝试其他代码时:

// Type your code here, or load an example.
int mod(int num) {
    return num & 0xFF;
}

mod(int):
    movzx   eax, dil
    ret
Run Code Online (Sandbox Code Playgroud)

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

gna*_*729 228

这是不一样的.尝试num = -79,您将从两个操作中获得不同的结果.(-79) % 256 = -79,虽然(-79) & 0xff是一些正数.

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

PS-有人评论道

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

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


Ral*_*zky 53

简短的回答

-1 % 256产率-1,而不是255它是-1 & 0xFF.因此,优化将是不正确的.

答案很长

C++的惯例(a/b)*b + a%b == a似乎很自然.a/b始终返回没有小数部分的算术结果(截断为0).因此,a%b具有与a0 相同的符号.

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

N4606开始,C++标准[expr.mul§4]中的相关部分指出:

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

启用优化

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

unsigned(-1)%256 == 0xFF
Run Code Online (Sandbox Code Playgroud)

另请参见.

其他语言

这可以在不同的编程语言中处理得非常不同,因为您可以在维基百科上查找.


Bat*_*eba 50

从C++ 11开始,num % 256如果num是负数则必须是非正数.

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

如果num在你的情况下,那将是另一回事unsigned:这些天我几乎期望编译器进行你引用的优化.

  • 几乎但并不完全.如果`num`为负数,那么`num%256`为零或负数(又称非正数). (6认同)
  • @Michael:除了%不是模运算符,它是*余数*运算符. (6认同)
  • 哪个IMO,在标准中是错误的:数学模运算应该采用除数的符号,在这种情况下为256.为了理解为什么考虑`( - 250 + 256)%256 == 6`,但是`(-250%256)+(256%256)`必须是,根据标准,"非正",因此不是'6`.像这样破坏关联性具有现实生活中的副作用:例如,当计算"缩小"整数坐标中的渲染时,必须移动图像以使所有坐标都为非负的. (5认同)
  • @Michael Modulus从来没有对加法进行分配("关联"是这个属性的错误名称!),即使你遵循数学定义到字母.例如,`(128 + 128)%256 == 0`但是`(128%256)+(128%256)== 256`.也许对指定的行为有一个很好的反对意见,但我不清楚它是你所说的那个. (2认同)

Che*_*Alf 11

我对编译器的推理没有心灵感知的洞察力,但是在%有必要处理负值(并且将分数舍入为零)的情况下,&结果总是低8位.

sar指令听起来像"右移算术",用符号位值填充空出的位.