use*_*913 4 c algorithm math x86 integer-arithmetic
C语言有签名和无符号类型,如char和int.我不确定,它是如何在汇编级别实现的,例如在我看来,有符号和无符号的乘法会带来不同的结果,因此汇编执行无符号和有符号算术或仅执行一次,这在某种程度上是模拟的不同的情况?
har*_*old 11
如果你看看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无符号乘法)请注意,只有"加宽"乘法具有无符号对应项.中间标有星号的两个表单都是有符号和无符号乘法,因为对于没有得到额外"上部"的情况,这是同样的事情.
"拓宽"乘法在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;
}
Run Code Online (Sandbox Code Playgroud)
使用GCC或其他一些相对合理的编译器,你会得到这样的东西:
test(unsigned int, unsigned int):
mov eax, edi
imul eax, esi
ret
test(int, int):
mov eax, edi
imul eax, esi
ret
Run Code Online (Sandbox Code Playgroud)
(带-O1的实际GCC输出)
因此,对于乘法(至少不是在C中使用的乘法类型)和其他一些操作,签名无关紧要,即:
x86不为那些提供单独的签名/未签名版本,因为无论如何都没有区别.
但是对于某些操作存在差异,例如:
idivvs div)idivvs div)sarvs shr)(但要注意C中签名的右移)但最后一个是特殊的,x86也没有单独的signed和unsigned版本,而是有一个操作(cmp实际上只是一个非破坏性的sub)同时执行两个操作,并给出了几个结果(多个位) "旗帜"受到影响.后来实际使用这些标志(分支,条件移动setcc)的指令然后选择他们关心的标志.所以,例如,
cmp a, b
jg somewhere
Run Code Online (Sandbox Code Playgroud)
somewhere如果a"签署大于" 将会去b.
cmp a, b
jb somewhere
Run Code Online (Sandbox Code Playgroud)
somewhere如果a是"未签名以下" 将会去b.
有关标志和分支的更多信息,请参阅CMP后的汇编 - JG/JNLE/JL/JNGE.
这不是签名和无符号乘法相同的正式证据,我只是试着让你深入了解为什么它们应该是相同的.
考虑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
Run Code Online (Sandbox Code Playgroud)
2*3 = 6所以一切都检查出来.这只是大多数人在学校学习的常规长乘法,只有二进制,这使得它更容易,因为你不必乘以十进制数字,你只需要乘以0或1,然后移位.
无论如何,现在采取负数.符号位的权重为-8,因此在某一点上您将生成部分产品-8 * something.乘以8是左移3,所以前lsb现在是msb,所有其他位都是0.现在如果你否定它(毕竟它是-8,而不是8),没有任何反应.零显然没有变化,但是8也是如此,并且通常只有msb设置的数字:
-1000 = ~1000 + 1 = 0111 + 1 = 1000
Run Code Online (Sandbox Code Playgroud)
因此,如果msb的权重为8(如无符号情况下)而不是-8,那么您已经完成了相同的操作.
大多数现代处理器都支持有符号和无符号算术。对于那些不支持的算术,我们需要模拟算术。
引用X86 架构的这个答案
首先,x86 原生支持有符号数的二进制补码表示。您可以使用其他表示,但这需要更多指令,并且通常会浪费处理器时间。
我所说的“本地支持”是什么意思?基本上我的意思是有一组用于无符号数的指令和另一组用于有符号数的指令。无符号数可以与有符号数位于相同的寄存器中,实际上您可以混合有符号和无符号指令,而不必担心处理器。由编译器(或汇编程序员)来跟踪数字是否有符号,并使用适当的指令。
首先,二进制补码具有加减法与无符号数相同的性质。数字是正数还是负数没有区别。(因此,您只需继续添加和订阅您的号码,而无需担心。)
当涉及到比较时,差异开始显现。x86 有一种简单的区分方法:高于/低于表示无符号比较,大于/小于表示有符号比较。(例如,JAE 的意思是“如果高于或等于则跳转”并且未签名。)
还有两组乘法和除法指令来处理有符号和无符号整数。
最后:如果你想检查,比如说,溢出,你会对有符号和无符号数字做不同的事情。
| 归档时间: |
|
| 查看次数: |
4261 次 |
| 最近记录: |