Assembly h表示i+j=k加上最终进位

Assembly h表示i+j=k加上最终进位,assembly,x86,x86-64,multiplication,unsigned,Assembly,X86,X86 64,Multiplication,Unsigned,ck=∑i、 j=0..n;i+j=kai·bj·2i+j+Ck 术语Ck是进位,当它向高位传播时,它只取决于低位。 第二项不能有i或j>k的ai或bj,就好像第一项为真,然后i=k+e,对于正的、非空的e,因此j=k-i=k-k-e=-e 但是j不能是负的 第二种情况类似,留给读者 附录B 正如BeeOnRope在评论中指出的,如果只需要部分结果,处理器可能不会计算完整结果 你可能是说这只是一种概念上的思考方式。当您使用64x64->64格式时,处理器不必执行完整的128位乘法。事实上,在最近

ck=∑i、 j=0..n;i+j=kai·bj·2i+j+Ck

术语Ck是进位,当它向高位传播时,它只取决于低位。
第二项不能有i或j>k的ai或bj,就好像第一项为真,然后i=k+e,对于正的、非空的e,因此j=k-i=k-k-e=-e
但是j不能是负的
第二种情况类似,留给读者

附录B 正如BeeOnRope在评论中指出的,如果只需要部分结果,处理器可能不会计算完整结果

你可能是说这只是一种概念上的思考方式。当您使用64x64->64格式时,处理器不必执行完整的128位乘法。事实上,在最近的Intel上,截断的形式只需要1个uop,但完整的形式需要2个uop,因此需要做一些额外的工作

此外,符号扩展可能在概念上也是如此


类似地,符号扩展可能“在概念上”发生,但可能不会在硬件中发生。他们不会有额外的电线和晶体管来做符号或零扩展,这会给已经很大的乘法器增加很多体积,但是会使用一些其他的技巧来做乘法,就好像发生了那样



†长度为n的二进制数的数量级为2n,因此两个此类数的乘法数量级为2n·2n=2n+n=22 n。就像长度为2 n的数字一样。

我假设这是针对x86的?是的,我可以选择更好的标记:)两个操作数
imul
只返回乘积的下半部分,因此符号性不受关注。哪个位是符号位?最重要的位,其权重为-2^(k-1)而不是+2^(k-1)根据哈罗德的说法,fun2也是32位乘法而不是64位乘法?但是
fun2
也使用
imul
,但采用无符号整数。我的意思是它是64x64->64,而不是64x64->128。签名只影响128位productGreat答案的上半部分!我想你可能想澄清一下,当你说这样的话时,不管使用什么形式,处理器总是以两倍于操作数的大小计算结果(即,像第一种形式),你可能的意思是这只是一种概念上的思考方式。当您使用64x64->64格式时,处理器不必执行完整的128位乘法。事实上,在最近的Intel上,截断形式只需要1个uop,但完整形式需要2个uop,因此需要做一些额外的工作。类似地,符号扩展可能“在概念上”发生,但可能不会在硬件中发生。他们不会有额外的电线和晶体管来做符号或零扩展,这会给已经很大的乘法器增加很多体积,但会使用其他一些技巧来做乘法,就好像发生了那样。@BeeOnRope:我认为
imul r64
中的额外uop只是写高半部;UOP在Intel uarches中最多可以有一个非标志输出。而
imul r32
必须对乘法器输出进行拆分和/或零扩展,使其不会自然拆分(在64位边界处),因此为3 UOP。更新了Margaret的答案,添加了一个TL:DR和一些uop。@PeterCordes是的,我想你肯定需要一个额外的uop来写入高半部,但我不清楚是否调用了整个64->128电路,而忽略了高输出。宽乘法肯定比窄乘法更昂贵,但不清楚是否值得仅仅为了节省一点功率而抑制结果的高部分(或者在乘法器的设计中这是否可行)@BeeOnRope:如果为了节省电能,晶体管的位翻转只会导致高半衰期,我一点也不会感到惊讶,特别是对于
vpmullw
vs.
vpmulhw
和integer。可能不是电源门控高半部,因为它都是连接的。对于窄输入,128不翻转的高64可能会自然发生,例如对于
mul r32
,但是
imul r32、r32
imul r64、r64
的进一步选通是可能的。但这只是电源方面的考虑,而不是时钟对时钟/uop性能,这正是我在编辑中试图指出的。
#include <stdint.h>

uint64_t usquare(uint32_t x) {
  return (uint64_t)x * (uint64_t)x;
}
 0: 89 f8                   mov    eax,edi
 2: 48 0f af c0             imul   rax,rax
 6: c3                      ret  
0:  48 0f af ff             imul   rdi,rdi
4:  48 89 f8                mov    rax,rdi
7:  c3                      ret  
#include <stdint.h>

uint64_t fun0 ( uint32_t x )
{
    return (uint64_t)x * (uint64_t)x;
}
uint64_t fun1 ( uint32_t x )
{
    return ((uint64_t)x) * ((uint64_t)x);
}
uint64_t fun2 ( uint64_t x )
{
    return (x * x);
}



0000000000000000 <fun0>:
   0:   89 f8                   mov    %edi,%eax
   2:   48 0f af c0             imul   %rax,%rax
   6:   c3                      retq   
   7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
   e:   00 00 

0000000000000010 <fun1>:
  10:   89 f8                   mov    %edi,%eax
  12:   48 0f af c0             imul   %rax,%rax
  16:   c3                      retq   
  17:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  1e:   00 00 

0000000000000020 <fun2>:
  20:   48 89 f8                mov    %rdi,%rax
  23:   48 0f af c7             imul   %rdi,%rax
  27:   c3                      retq   
0x00FF * 0x00FF = 0xFE01
0xFFFF * 0xFFFF = 0xFFFE0001
so
0xFF * 0xFF = 0x01
imul ax         ;16x16->32, Result is dx:ax
imul rax        ;64x64->128, Result is rdx:rax 
imul ax, ax          ;16x16->16, Lower WORD of the result is ax
imul rax, rax        ;64x64->64, Lower QWORD of the result is rax 
     How mul extends              How imul extends       
       and operand                  and operand

     +----+       +----+          +----+       +----+
     |0...|       |1...|          |0...|       |1...|
     +----+       +----+          +----+       +----+  

+----+----+  +----+----+     +----+----+  +----+----+
|0000|0...|  |0000|1...|     |0000|0...|  |1111|1...|
+----+----+  +----+----+     +----+----+  +----+----+
#include <stdint.h>

uint64_t foo(uint32_t a)
{
    return a*(uint64_t)a;
}
foo(unsigned int):
        mov     eax, edi        ;edi = a
        imul    rax, rax        ;64x64->64
        ret
foo(unsigned int):
        mov     eax, DWORD PTR [esp+4]
        mul     eax
        ret