【问题标题】:Why is (a % 256) different than (a & 0xFF)?为什么 (a % 256) 与 (a & 0xFF) 不同?
【发布时间】:2017-03-21 20:44:24
【问题描述】:

我一直认为在执行(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

似乎我完全错过了一些东西。 有什么想法吗?

【问题讨论】:

  • 0xFF 是 255 而不是 256。
  • @RishikeshRaje:所以? % 也不是 &
  • @RishikeshRaje:我相信 OP 非常清楚这一点。它们用于不同的操作。
  • 出于兴趣,如果numunsigned,你会得到更好的结果吗?
  • @RishikeshRaje 按位和 0xFF 等效于无符号整数的模 2^8。

标签: c++ optimization


【解决方案1】:

简答

-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】:

    不一样。试试num = -79,这两种操作你会得到不同的结果。 (-79) % 256 = -79,而(-79) & 0xff 是某个正数。

    使用unsigned int,操作相同,代码可能相同。

    PS-有人评论了

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

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

    【讨论】:

    【解决方案3】:

    从 C++11 开始,如果 num 为负数,num % 256 必须为非正数。

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

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

    【讨论】:

    • 差不多但不完全。如果num 为负数,则num % 256 为零或负数(也称为非正数)。
    • Which IMO,是标准中的一个错误:数学上的模运算应该取除数的符号,在这种情况下为 256。为了理解为什么考虑(-250+256)%256==6,但根据标准,(-250%256)+(256%256) 必须是“非正数”,因此不是6。像这样破坏关联性会产生现实生活中的副作用:例如,在整数坐标中计算“缩小”渲染时,必须将图像移动到所有坐标都为非负数。
    • @Michael Modulus 从来没有分配过加法(“关联”是这个属性的错误名称!),即使您严格遵循数学定义。例如,(128+128)%256==0(128%256)+(128%256)==256。也许对指定的行为有很好的反对意见,但我不清楚这就是你所说的。
    • @DanielWagner,你是对的,当然,我误会了“联想”。但是,如果一个人保持除数的符号并以模算术计算所有内容,则分配属性确实成立;在您的示例中,您将拥有256==0。关键是在模 N 算术中精确地具有 N 可能值,这只有在所有结果都在 0,...,(N-1) 范围内而不是 -(N-1),...,(N-1) 范围内时才有可能。
    • @Michael:除了 % 不是模运算符,它是 remainder 运算符。
    【解决方案4】:

    我对编译器的推理没有心灵感应的洞察力,但在 % 的情况下,有必要处理负值(并且除法四舍五入到零),而对于 &,结果始终是低 8 位。

    sar 指令在我看来就像“算术右移”,用符号位值填充空出的位。

    【讨论】:

      猜你喜欢
      • 2021-10-06
      • 2019-04-30
      • 2017-05-19
      • 2014-03-29
      • 2015-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      相关资源
      最近更新 更多