2013-06-01 70 views
1

是否有一個x86內聯asm解決方案可以採用雙倍數乘以100.00,然後轉換爲整數。 「輸入」雙重實際上是一個價格,我想轉換爲「美分」作爲一個整數。是否有內聯x86 asm可以比gcc更快?

可以做出的假設。

  • 這個double不會是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

我要繼續前進,寫一個轉換功能假設輸入小於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,不是嗎?如果沒有,沒關係。

+0

謝謝你的深思熟慮的答案。在我有限的測試中,它看起來像是一樣,如果可能只是一個時鐘滴答較慢。我一直在想,必須有一種方法來設置FP四捨五入寄存器,以便通過mul和cvtsd2si來完成它。 –

+0

@EricJohnson我已經添加了一個方法,應該編譯乘以100.,加法和cvtsd2si。 –

+0

對我來說,第二美分的實現產生了與第一個相同的x86,但也許我犯了一個錯誤,並會再次嘗試。 –