c - x86 上的有符号和无符号算术实现

标签 c algorithm math x86 integer-arithmetic

C 语言有符号和无符号类型,如 char 和 int。 我不确定它是如何在汇编级别实现的,因为 例如,在我看来,有符号和无符号的乘法 会带来不同的结果,所以汇编都做无符号的 和带符号的算术或只有一个,这在某种程度上 模拟不同的情况?

最佳答案

如果你看x86的各种乘法指令,只看32位的变体而忽略BMI2,你会发现这些:

  • imul r/m32(32x32->64 有符号乘法)
  • imul r32, r/m32(32x32->32 乘法)*
  • imul r32, r/m32, imm(32x32->32 相乘)*
  • mul r/m32(32x32->64 无符号乘法)

请注意,只有“扩大”乘法具有无符号的对应项。中间标有星号的两种形式,都是有符号和无符号的乘法,因为如果你没有得到额外的“上半部分”,那是一回事。 p>

“扩大”乘法在 C 语言中没有直接等价物,但编译器无论如何都可以(而且经常这样做)使用这些形式。

例如,如果你编译这个:

uint32_t test(uint32_t a, uint32_t b)
{
    return a * b;
}

int32_t test(int32_t a, int32_t b)
{
    return a * b;
}

使用 GCC 或其他一些相对合理的编译器,你会得到这样的东西:

test(unsigned int, unsigned int):
    mov eax, edi
    imul    eax, esi
    ret
test(int, int):
    mov eax, edi
    imul    eax, esi
    ret

(使用 -O1 的实际 GCC 输出)


因此符号性对于乘法(至少对于您在 C 中使用的那种乘法而言不是)和其他一些操作无关紧要,即:

  • 加减法
  • 按位与、或、异或、非
  • 否定
  • 左移
  • 比较平等

x86 不为这些提供单独的签名/未签名版本,因为无论如何都没有区别。

但对于某些操作是有区别的,例如:

  • 划分(idivdiv)
  • 剩余部分(也是 idivdiv)
  • 右移(sar vs shr​​)(但要注意 C 语言中的有符号右移)
  • 比较大于/小于

但最后一个很特别,x86 也没有单独的有符号和无符号版本,而是只有一个操作(cmp,它实际上只是一个非破坏性的 sub ) 同时执行这两项操作,并给出多个结果(“标志”中的多个位受到影响)。实际使用这些标志的后续指令(分支、条件移动、setcc)然后选择它们关心的标志。例如,

cmp a, b
jg somewhere

如果 a 的“符号大于”b,将去某处

cmp a, b
jb somewhere

如果 a 是“下面未签名的”b,将去某处

参见 Assembly - JG/JNLE/JL/JNGE after CMP有关标志和分支的更多信息。


这不是有符号乘法和无符号乘法相同的正式证明,我只是试图让您深入了解为什么它们应该相同。

考虑 4 位 2 的补码整数。它们各个位的权重,从 lsb 到 msb,1、2、4 和 -8。当你将这些数字中的两个相乘时,你可以将其中一个分解为对应于它的位的 4 个部分,例如:

0011 (decompose this one to keep it interesting)
0010
---- *
0010 (from the bit with weight 1)
0100 (from the bit with weight 2, so shifted left 1)
---- +
0110

2 * 3 = 6 所以一切都检查出来了。这只是大多数人在学校学习的常规长乘法,只有二进制,这使得它变得容易得多,因为你不必乘以十进制数字,你只需要乘以 0 或 1,然后移位。

无论如何,现在取一个负数。符号位的权重是 -8,所以在某个时候你会得到一个部分乘积 -8 * something。 8 的乘法是左移 3,所以之前的 lsb 现在是 msb,所有其他位都是 0。现在如果你否定它(毕竟是 -8,而不是 8),什么也不会发生。零显然没有变化,但8也是如此,一般只设置了msb的数字:

-1000 = ~1000 + 1 = 0111 + 1 = 1000

因此,如果 msb 的权重为 8(如在无符号情况下)而不是 -8,那么您所做的事情与您所做的相同。

关于c - x86 上的有符号和无符号算术实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25241477/

相关文章:

c - 正在读取文件,但未保存

c - Ada 到 C : Value of argument changes on imported function across languages

java - 智能算法在范围内随机化 Double 但有赔率

algorithm - Big O Notation - 自然数 M 和常数因子 C 是什么意思?

java - 获取 NaN 作为简单 Java 程序的答案

c - 在 C 中创建普通 GUI 的最简单方法是什么?

c - 意外标记附近的语法错误

python - 在 Python 中计算以 10 为底的对数的算法

java - for 循环与 if-else 语句中的代码

python - "Quantize"舍入数字 Python 3.2