我要繼續前進,寫一個轉換功能假設輸入小於2 ^百分之五十二:
#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
而不是double
,在this post中)。作爲交換,您必須放棄將關係(0.125
,0.625
,...)從零圓整的財產:通過使用0x1.fffffffffffffp-2
他們被舍入。
你知道爲什麼我的例子來說明關係是0.125
而不是0.005
,不是嗎?如果沒有,沒關係。
謝謝你的深思熟慮的答案。在我有限的測試中,它看起來像是一樣,如果可能只是一個時鐘滴答較慢。我一直在想,必須有一種方法來設置FP四捨五入寄存器,以便通過mul和cvtsd2si來完成它。 –
@EricJohnson我已經添加了一個方法,應該編譯乘以100.,加法和cvtsd2si。 –
對我來說,第二美分的實現產生了與第一個相同的x86,但也許我犯了一個錯誤,並會再次嘗試。 –