32位整数乘法指令实现64位乘法
思路就是将64位数分解为两个32位数
(AB) * (CD) = A*C * (1<<64) + ( A*D + B*C )*(1<<32) + B*D
由于计算结果也用64位数保存, 所以A*C可以丢弃了, 不用做这多余的计算, 总共需要3次32位乘法
具体实现如下(同样适用于int64_t):
#include <stdint.h>
typedef union {
uint64_t qword;
struct {
int low;
int high;
} ddword;
} ii_t;
uint64_t mul(uint64_t a, uint64_t b) {
ii_t x = (ii_t)a, y = (ii_t)b;
__asm__(
"movl %0, %%ecxnt"
"imull %3, %%ecxnt"
"movl %2, %%eaxnt"
"mull %1nt"
"addl %%eax, %%ecxnt"
"movl %3, %%eaxnt"
"mull %1nt"
"addl %%ecx, %%edxnt"
:"=m"(x.ddword.high),
"=m"(x.ddword.low),
"=m"(y.ddword.high),
"=m"(y.ddword.low)
);
}
其中第15, 17, 20行总共使用3次乘法指令 , 前两个对应的是A*D和B*C, 最后一个对应B*D
32位Windows和Linux都使用edx:eax保存64位返回值
将(A*D+B*C)截断的值与B*D进位的和, 保存在edx中, B*D的32位截断值保存在eax中
因为无符号乘法和补码乘法的截断结果一致, A*D和B*C的进位也可以丢弃, 所以这两次乘法使用mull和imull均可
而计算B*D的进位需要保存, 所以必须使用mull指令, imull会得到错误的进位