Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C x86上有符号和无符号算术的实现_C_Algorithm_Math_X86_Integer Arithmetic - Fatal编程技术网

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

C x86上有符号和无符号算术的实现,c,algorithm,math,x86,integer-arithmetic,C,Algorithm,Math,X86,Integer Arithmetic,C语言有符号和无符号类型,如char和int。 例如,我不确定它是如何在汇编级别实现的 在我看来,有符号和无符号的乘法 将产生不同的结果,因此汇编程序会同时执行未签名和 有符号算术或者只有一个,这在某种程度上是 针对不同情况进行仿真?大多数现代处理器都支持有符号和无符号算术。 对于那些不受支持的算法,我们需要模拟该算法 引用X86体系结构的 首先,x86对两者的补充具有本机支持 有符号数字的表示。可以使用其他表示法 但这需要更多的指导,通常是浪费时间 处理器时间 我说的“本地支持”是什么意思?基

C语言有符号和无符号类型,如char和int。 例如,我不确定它是如何在汇编级别实现的 在我看来,有符号和无符号的乘法 将产生不同的结果,因此汇编程序会同时执行未签名和 有符号算术或者只有一个,这在某种程度上是
针对不同情况进行仿真?

大多数现代处理器都支持有符号和无符号算术。 对于那些不受支持的算法,我们需要模拟该算法

引用X86体系结构的

首先,x86对两者的补充具有本机支持 有符号数字的表示。可以使用其他表示法 但这需要更多的指导,通常是浪费时间 处理器时间

我说的“本地支持”是什么意思?基本上我的意思是有一个 用于无符号数字的指令集和另一组 您可以将其用于有符号的数字。未签名的数字可以放在同一个位置 注册为带符号的数字,实际上,您可以混合使用带符号和 无需担心处理器的未签名指令。这取决于 编译器(或汇编程序员)来跟踪一个数字是否为 是否签名,并使用适当的说明

首先,二的补数具有加法和加法的性质 减法与无符号数字的减法相同。没有关系 数字是正数还是负数。(那么你就 继续添加和细分您的数字,无需担心。)

当涉及到比较时,差异开始显现出来。x86有一个 区分它们的简单方法:上/下表示未签名 比较和大于/小于表示有符号比较。(例如。 JAE表示“高于或等于时跳转”,不带符号。)

还有两组乘法和除法指令用于 处理有符号和无符号整数

最后:如果你想检查,比如说,溢出,你会这样做 有符号和无符号数字的情况不同


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

  • imulr/m32
    (32x32->64符号乘法)
  • imulr32,r/m32
    (32x32->32乘法)*
  • imulr32,r/m32,imm
    (32x32->32乘法)*
  • mulr/m32
    (32x32->64无符号乘法)
请注意,只有“加宽”乘法具有无符号对应项。中间的两种形式,用星号标记,都是有符号的和无符号的乘法,因为如果你没有得到额外的“上部分”,那就是同样的事情。

“加宽”乘法在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中使用的乘法类型)和其他一些运算并不重要,即:

  • 加减法
  • 按位AND、OR、XOR、NOT
  • 否定
  • 左移
  • 平等比较
x86不提供单独的有符号/无符号版本,因为两者没有区别

但对于某些操作,存在差异,例如:

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
  • 部门(
    idiv
    vs
    div
  • 剩余部分(也包括
    idiv
    vs
    div
  • 右移(
    sar
    vs
    shr
    )(但要注意C中的有符号右移)
  • 比较大于/小于
但最后一个是特殊的,x86也没有单独的有符号和无符号版本,相反,它有一个操作(
cmp
,它实际上只是一个非破坏性的
sub
),同时执行这两个操作,并给出多个结果(“标志”中的多个位受到影响)。后面的指令实际使用这些标志(分支、条件移动、
setcc
),然后选择它们关心的标志。那么比如说,

cmp a, b
jg somewhere
如果
a
被“签名大于”
b
,将转到
某个地方

cmp a, b
jb somewhere
如果
a
b
下面是“未签名的”,就会转到
某个地方

cmp a, b
jb somewhere
有关标志和分支的更多信息,请参见


这并不是有符号乘法和无符号乘法相同的正式证明,我只是想让你了解为什么它们应该是相同的

考虑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*某物
。8的乘法是左移3,因此前一个lsb现在是msb,所有其他位都是0。现在如果你否定它(毕竟是-8,不是8),什么也不会发生。0显然是不变的,但8也是不变的,并且通常只有msb设置的数字:

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

因此,如果msb的权重是8(如未签名的情况)而不是-8,那么您已经做了与之相同的事情。

这是对
cmp
sub
的一点补充。我们知道
cmp
被认为是非破坏性的
sub
,所以让我们关注
sub

当x86 cpu执行
subebx:15(dec) - eax: 1(dec) = 14(dec) = 0b1110 (two's complement)
ebx: -1(dec) - eax: 1(dec) = -2(dec) = 0b1110 (two's complement)