高效计算32位整数乘法的高阶位

时间:2009-09-08 23:54:37

标签: c++ c optimization

许多CPU具有单个汇编操作码,用于返回32位整数乘法的顺序位。通常将两个32位整数相乘会产生64位结果,但如果将其存储为32位整数,则会将其截断为低32位。

例如,在PowerPC上,mulhw操作码在一个时钟内返回32位32位乘法的64位结果的高32位。这正是我正在寻找的,但更便携。在NVidia CUDA中有一个类似的操作码,umulhi()。

在C / C ++中,有没有一种有效的方法来返回32x32乘法的高阶位? 目前我通过转换为64位来计算它,例如:

unsigned int umulhi32(unsigned int x, unsigned int y)
{
  unsigned long long xx=x;
  xx*=y;
  return (unsigned int)(xx>>32);
}

但这比常规的32乘32乘以慢11倍,因为即使是乘法,我也使用过度的64位数学运算。

有更快的方法来计算高阶位吗?

这显然是最好用BigInteger库解决(这是一种过度杀伤并且会产生巨大的开销)。

SSE似乎有PMULHUW,16x16 - >最高16位版本,但不是32x32 - >像我正在寻找的前32版。

3 个答案:

答案 0 :(得分:13)

gcc 4.3.2,使用-O1优化或更高版本,将您的功能完全翻译为IA32程序集,如下所示:

umulhi32:
        pushl   %ebp
        movl    %esp, %ebp
        movl    12(%ebp), %eax
        mull    8(%ebp)
        movl    %edx, %eax
        popl    %ebp
        ret

只执行一个32位mull并将结果的高32位(来自%edx)放入返回值。

这就是你想要的,对吗?听起来你只需要在编译器上打开优化;)你可以通过消除中间变量来推动编译器正确的方向:

unsigned int umulhi32(unsigned int x, unsigned int y)
{
  return (unsigned int)(((unsigned long long)x * y)>>32);
}

答案 1 :(得分:3)

我认为在标准C / C ++中没有比现有方法更好的方法。我要做的是写一个简单的程序集包装器,它返回你想要的结果。

不是说你问的是Windows,但是作为一个例子,即使Windows有一个听起来像你想要的API(32位乘32位乘以获得完整的64位结果),它实现了乘法作为一个宏,可以做你正在做的事情:

#define UInt32x32To64( a, b ) (ULONGLONG)((ULONGLONG)(DWORD)(a) * (DWORD)(b))

答案 2 :(得分:2)

在32位intel上,乘法会影响两个输出寄存器。也就是说,无论您是否想要,64位都是完全可用的。它只是编译器是否足够智能以利用它的功能。

现代编译器做了很多令人惊奇的事情,所以我的建议是更多地尝试优化标记,至少在英特尔上。您可能认为优化器可能知道处理器从32乘32位产生64位值。

也就是说,在某些时候,我试图让编译器在分割结果上使用模数和除数,但1998年的旧Microsoft编译器不够聪明,无法实现相同的指令产生两种结果。 / p>