是否有内联x86 asm可以比gcc的llround更快?

时间:2013-06-01 13:39:10

标签: gcc floating-point inline x86-64

是否有x86内联asm解决方案,可以采用双倍,100.00倍,然后转换为整数。 “输入”双精度实际上是一个价格,我想将整数转换为“美分”。

可以做出的假设。

  • 双重不会是NaN,Infinity或签名零。
  • 双重将是积极的
  • 转换可能需要进行一些舍入。例如:8.19999应该变成820 作为整数。
  • SSE4说明可用
  • 数据按顺序到达
  • GCC> = 4.7是首选的编译器。

换句话说,当使用gcc 4.7.x并使用-O3, - fast-math进行编译时,是否有一种x86 asm方法会比这种类型的代码更好?

#include <math.h>
int cents = llround(price*100.0);

1 个答案:

答案 0 :(得分:1)

我将继续编写转换函数,假设输入小于2 ^ 52/100:

#include <string.h>
#include <stdio.h>

/*@ requires 0 <= d < 0x1.0p52 ; */
long long cents(double d)
{
  d = d * 100. + 0x1.0p52;
  long long l;
  memcpy(&l, &d, sizeof(double));
  return l & 0xFFFFFFFFFFFFF;
}

int main()
{
  printf("%lld\n", cents(0.994));
  printf("%lld\n", cents(0.996));
  printf("%lld\n", cents(123456789.004));
  printf("%lld\n", cents(123456789.006));
}

预期结果是:

99
100
12345678900
12345678901

gcc -O2将我的函数cents()的计算部分编译为:

mulsd   LCPI1_0(%rip), %xmm0
addsd   LCPI1_1(%rip), %xmm0
movd    %xmm0, %rcx
movabsq $4503599627370495, %rax
andq    %rcx, %rax

您可能想要内联它或告诉编译器内联它。这可能会或可能不会比llround()更快,具体取决于您的处理器。

如果你有一个fuse-multiply-add指令可用,那么d * 100. + 0x1.0p52可以在一条指令中计算,但无论如何都要加载常量。如果你必须在循环中执行其中的许多操作,请将常量保留在寄存器中(或告诉编译器它可以这样做)。


另一种方法是添加0x1.fffffffffffffp-2(紧急double下面的0.5)并截断为long long

long long cents(double d) { return d * 100. + 0x1.fffffffffffffp-2; }

使用0x1.fffffffffffffp-2代替0.5的基本原理是,在所有情况下,它都会为您提供最接近的整数。相比之下,添加0.5会在某些情况下为您提供最接近的两个整数(详细信息,类型为float而不是double,位于this post)。作为交换,您必须放弃关系(0.1250.625,...)从零开始舍入的属性:通过使用0x1.fffffffffffffp-2,它们会向下舍入。

你知道为什么我说明关系的例子是0.125而不是0.005,不是吗?如果没有,请不要介意。