我将继续编写一个转换函数,假设输入小于 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()
取决于您的处理器更快。
如果您有可用的融合乘加指令,则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
中使用 type而不是)。作为交换,您必须放弃关系 ( , , ...) 从零四舍五入的属性:通过使用它们被四舍五入。double
0.125
0.625
0x1.fffffffffffffp-2
你知道为什么我的例子来说明关系是0.125
而不是0.005
,不是吗?如果没有,请不要介意。