签名和基于x86无符号运算的实现 [英] signed and unsigned arithmetic implementation on x86

查看:113
本文介绍了签名和基于x86无符号运算的实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C语言有符号和无符号类型,如char和int类型。 我不知道,它是如何在汇编级实现, 例如,在我看来那乘法符号和无符号 会带来不同的结果,所以组装一举两得签名 和符号的算术运算或只有一个,这是在某种方式 模拟了不同情况下?

C language has signed and unsigned types like char and int. I am not sure, how it is implemented on assembly level, for example it seems to me that multiplication of signed and unsigned would bring different results, so do assembly do both unsigned and signed arithmetic or only one and this is in some way emulated for the different case?

推荐答案

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

If you look at the various multiplication instructions of x86, looking only at 32bit variants and ignoring BMI2, you will find these:

  • IMUL R / M32 (32x32-> 64符号乘法)
  • IMUL R32,R / M32 (32x32-> 32乘)*
  • IMUL R32,R / M32,IMM (32x32-> 32乘)*
  • MUL R / M32 (32x32-> 64无符号乘法)
  • imul r/m32 (32x32->64 signed multiply)
  • imul r32, r/m32 (32x32->32 multiply) *
  • imul r32, r/m32, imm (32x32->32 multiply) *
  • mul r/m32 (32x32->64 unsigned multiply)

请注意,只有扩大乘法是无符号对应。这两种形式在中间,标有星号,是符号和无符号乘法,因为在这里你不会获得额外的上部的情况下,的这是同样的事情的。

Notice that only the "widening" multiply has an unsigned counterpart. The two forms in the middle, marked with an asterisk, are both signed and unsigned multiplication, because for the case where you don't get that extra "upper part", that's the same thing.

在扩大乘法没有直接等价于C,但是编译器可以(并且经常这样做),无论如何使用这些格式。

The "widening" multiplications have no direct equivalent in C, but compilers can (and often do) use those forms anyway.

例如,如果编译如下:

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或其他一些相对合理的编译器,你会得到这样的:

With GCC or some other relatively reasonable compiler, you'd get something like this:

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

(实际GCC输出-O1)

(actual GCC output with -O1)

所以,符号性不要紧,乘法(至少不是那种在C用乘法来算的)和一些其他的操作,即:

So signedness doesn't matter for multiplication (at least not for the kind of multiplication you use in C) and for some other operations, namely:

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

86不提供单独的无/有符号版本的,因为有没有区别呢。

x86 doesn't offer separate signed/unsigned versions for those, because there's no difference anyway.

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

But for some operations there is a difference, for example:

  • 师( IDIV VS DIV
  • 在剩下的(也 IDIV VS DIV
  • 右键移位(特区 VS SHR )(但要注意签署权转移的C)
  • 在比较了大于/小于
  • division (idiv vs div)
  • remainder (also idiv vs div)
  • right shift (sar vs shr) (but beware of signed right shift in C)
  • comparing for bigger than / smaller than

但是,最后一个是特殊的,X86没有单独的版本签名和未签名的这两种,而是有一个操作( CMP ,这其实只是无损),做两者兼而有之,并给出了几个结果(多个位的标志受到影响)。后来说明实际使用这些标志(分公司,有条件的举动, setcc ),然后选择他们所关心的这些标志。因此,例如,

But that last one is special, x86 doesn't have separate versions for signed and unsigned of this either, instead it has one operation (cmp, which is really just a nondestructive sub) that does both at once, and gives several results (multiple bits in "the flags" are affected). Later instructions that actually use those flags (branches, conditional moves, setcc) then choose which flags they care about. So for example,

cmp a, b
jg somewhere

会去地方如果 A 是签订大于 B

cmp a, b
jb somewhere

会去地方如果 A 是无符号下面的 B

Would go somewhere if a is "unsigned below" b.

请参阅大会 - JG / JNLE / JL / CMP 了解更多关于标志和树枝后JNGE

See Assembly - JG/JNLE/JL/JNGE after CMP for more about the flags and branches.

这会不会是签订正式的证明和无符号乘法是相同的,我就尝试给你深入了解他们为什么应该是一样的。

This won't be a formal proof that signed and unsigned multiplication are the same, I'll just try to give you insight into why they should be the same.

考虑4位二进制补码整数。权重其各个位是,从LSB到MSB,1,2,4,和-8。当你将两个这些数字,就可以分解其中的一成四部分对应其位,例如:

Consider 4-bit 2's-complement integers. The weights their individual bits are, from lsb to msb, 1, 2, 4, and -8. When you multiply two of those numbers, you can decompose one of them into 4 parts corresponding to its bits, for example:

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,并转移到繁殖。

2 * 3 = 6 so everything checks out. That's just regular long multiplication that most people learn in school, only binary, which makes it a lot easier since you don't have to multiply by a decimal digit, you only have to multiply by 0 or 1, and shift.

不管怎样,现在就为负数。符号位的权重为-8,所以在一个点上,你会做出部分积 -8 *的东西。乘以8左移3,因此前者LSB现在是MSB,而所有其他位为0。现在,如果你否定了(这是-8毕竟不是8),没有任何反应。零显然是不变的,但这样是8,以及通常只有最高位组数:

Anyway, now take a negative number. The weight of the sign bit is -8, so at one point you will make a partial product -8 * something. A multiplication by 8 is shifting left by 3, so the former lsb is now the msb, and all other bits are 0. Now if you negate that (it was -8 after all, not 8), nothing happens. Zero is obviously unchanged, but so is 8, and in general the number with only the msb set:

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

所以,你做的,而不是-8同样的事情,你会(在无符号的情况下)做,如果最高位的权重为8。

So you've done the same thing you would have done if the weight of the msb was 8 (as in the unsigned case) instead of -8.

这篇关于签名和基于x86无符号运算的实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆