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

bxgwgixi  于 2023-02-17  发布在  其他
关注(0)|答案(5)|浏览(403)

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

我好像完全忽略了什么。有什么想法吗?

5uzkadbs

5uzkadbs1#

这是不一样的,尝试num = -79,你会从两个操作得到不同的结果。(-79) % 256 = -79,而(-79) & 0xff是某个正数。
使用unsigned int时,操作是相同的,代码也可能相同。

**PS-**有人评论

它们不应该相同,a % b被定义为a - b * floor (a / b)
这不是它在C、C++、Objective-C(即所有问题中的代码将编译的语言)中的定义。

9wbgstp7

9wbgstp72#

简短回答

-1 % 256产生-1,而不是255255-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&0xFF0xFF明显不同,因此编译器无法按照您想要的方式进行优化。
C++ standard [expr.mul §4] as of N4606中的相关章节规定:
对于整数操作数,/运算符产生丢弃任何小数部分的代商数;如果商a/b可表示为结果的类型,则(a/b)*b + a%b等于a [...]。

启用优化

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

unsigned(-1)%256 == 0xFF

另请参见此。

其他语言

不同的编程语言对此的处理方式有很大的不同,您可以在Wikipedia上查找。

x8diyxa7

x8diyxa73#

由于C++11,如果num为负,则num % 256必须为非正。
因此,位模式将取决于系统上有符号类型的实现:对于负的第一自变量,结果不是提取最低有效8位。
如果在您的例子中numunsigned,那就另当别论了:这些天来,我几乎 * 期望 * 编译器作出优化,你引用。

kgqe7b3p

kgqe7b3p4#

我对编译器的推理没有心灵感应的洞察力,但在%的情况下,有必要处理负值(除法向零舍入),而在&的情况下,结果总是低8位。
sar指令听起来像是“算术右移”,用符号位值填充空出的位。

v09wglhw

v09wglhw5#

正如其他人指出的那样:
如果你像我一样希望模运算实际上输出一个正整数,就像我转换色调的用例一样(HSV颜色空间)double其中1.0 == 360度通过乘以255.0到无符号字符,取地板,然后取模256。则&0xff是正确的优化。注意,我将255度视为360度,以适应本例中的unsigned char。

hue = [-1.0 , 2.0]
hue = (unsigned char)(floor(hue * 255.0) & 0xff)

但这可能不是适合您的特定用例的解决方案。

相关问题