Warm tip: This article is reproduced from serverfault.com, please click

assembly-为什么有必要在有符号除法的红利上加2的偏倚?

(assembly - Why is it necessary to add a bias to the dividend for signed division by a power of 2?)

发布于 2020-12-02 21:12:49

我正在“计算机系统-程序员的视角”中学习汇编代码,并遇到以下示例:

在以下C函数中,我们保留了操作OP的定义不完整:

#define OP

/* Unknown operator */
short arith(short x) {
    return x OP 16;
}

编译时,gcc生成以下汇编代码:

arith:
  leaq    15(%rdi), %rax
  testq   %rdi, %rdi
  cmovns  %rdi, %rax
  sarq    $4, %rax
  ret

OP是什么操作?

后来,这本书给出了以下答案:

运算符为“ /”。我们看到这是一个右移除以4的幂的示例(请参阅第2.3.7节)。在以k = 4移位之前,当股息为负时,我们必须添加(2 ^ k)− 1 = 15的偏差。

我知道编译器在这里使用优化,创建一个等于x + 15的临时变量,如果x小于零,则有条件地将该变量重新设置为x。我想知道的是为什么首先需要使用偏见。如果代码没有象前3个汇编步骤那样会发生什么呢?

  sarq    $4, %rax
  ret

认为答案是,我们需要摆脱负数的二进制补码符号位,才能得出正确的答案为零。例如,如果x = -12(即11110100),并且我们想除以4,则在不先添加偏置的情况下向右移4位将等于11111111(或十进制形式的-1),这不是预期的答案我希望将-12除以16所得的0之分。取而代之的是,我们将-12加15以得到3(又称00000011),然后我们可以将其右移4位以得到00000000,也就是0的正确答案十进制形式。

以上解释正确吗?还是在偏见的使用方式和原因方面我错过了标记?

更新-显然我正在使用的书中的示例汇编代码是不正确的。这是正确的程序集:

    arith:
        testw   %di, %di
        leal    15(%rdi), %eax
        cmovns  %edi, %eax
        sarw    $4, %ax
        ret

关于为什么需要偏见的更大问题仍然存在。是因为在不先加上偏见的情况下移位负数会产生我提到的错误结果吗?

Questioner
Richie Thomas
Viewed
0
chqrlie 2021-01-04 17:11:09

使用二进制补码表示的右算术移位负值执行整数除以2的幂,并朝着负无穷大舍入。这不是C中整数除法的语义,在整数除法中必须向0舍入。

为了实现16的有符号除法,编译器在分子为负数时将分子偏移15,然后将算术右移4:

arith:
    testw   %di, %di           // testing numerator
    leal    15(%rdi), %eax     // computing numerator+15 into %eax, flags unchanged
    cmovns  %edi, %eax         // conditional move depending if numerator was negative
    sarw    $4, %ax            // arithmetic right shift by 4 positions
    ret

该代码等效于此:

short div16(short num) {
    return (num < 0 ? num + 15 : num) >> 4;
}