是否有x86内联asm解决方案,可以采用双倍,100.00倍,然后转换为整数。 “输入”双精度实际上是一个价格,我想将整数转换为“美分”。
可以做出的假设。
换句话说,当使用gcc 4.7.x并使用-O3, - fast-math进行编译时,是否有一种x86 asm方法会比这种类型的代码更好?
#include <math.h>
int cents = llround(price*100.0);
答案 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.125
,0.625
,...)从零开始舍入的属性:通过使用0x1.fffffffffffffp-2
,它们会向下舍入。
你知道为什么我说明关系的例子是0.125
而不是0.005
,不是吗?如果没有,请不要介意。