2010-05-12 34 views
9

在Redis(http://code.google.com/p/redis)中將雙精度轉換爲整數以便將元素與元素相關聯,以便將此元素進行排序。即使許多用戶實際按整數排序(例如unix時間),該分數也是雙打的。爲了獲得速度

當數據庫被保存時,我們需要寫這個雙打ok磁盤。這是目前使用的內容:

snprintf((char*)buf+1,sizeof(buf)-1,"%.17g",val); 

此外還會檢查無窮大和非數字條件,以便在最終的數據庫文件中表示該條件。

不幸的是,將double轉換爲字符串表示法很慢。雖然我們在Redis中有一個以更快的方式將整數轉換爲字符串表示形式的函數。所以我的想法是檢查一個double是否可以被轉換成一個整數而不丟失數據,然後如果這是真的,則使用該函數將整數轉換爲一個字符串。

爲了提供一個很好的加速,當然整數「等價」的測試必須是快速的。所以我使用了一種可能未定義的行爲,但在實踐中效果很好。類似的東西:

double x = ... some value ... 
if (x == (double)((long long)x)) 
    use_the_fast_integer_function((long long)x); 
else 
    use_the_slow_snprintf(x); 

在我的推理上面的double casting將double轉換成long,然後返回到整數。如果範圍適合,並且沒有小數部分,則該數字將在轉換後存活,並且與初始數字完全相同。因爲我想確保這不會破壞某些系統中的某些東西,所以我加入了freenode上的#c,並受到很多侮辱;)因此,我現在正在嘗試這裏。

有沒有一種標準的方法來做我想要做的事情,而不需要去ANSI C之外?否則,上述代碼是否應該適用於當前Redis所針對的所有Posix系統?也就是說,Linux/Mac OS X/* BSD/Solaris現在正在運行的拱?

爲了使代碼更加完整,我可以添加的內容是在嘗試執行演員之前明確檢查雙精度的範圍。

謝謝你的幫助。

+0

侮辱侮辱,男人。我不知道答案,但我希望你找到答案。 – mmr 2010-05-12 17:06:03

+0

如果有幫助,http://stackoverflow.com/questions/638376/what-is-the-most-reliable-way-of-checking-if-a-floating-point-variable-is-an-inte was a在C#中檢查這種方式。我還沒有找到一個C版本。 – 2010-05-12 17:15:13

+0

或者,我可以使用modff()來檢查小數部分是否爲零?然後檢查整體部分的範圍是否在很長的範圍內,如果屬實,則施放它。 – antirez 2010-05-12 17:43:39

回答

6

也許一些舊的時尚定點數學可以幫助你。如果將雙精度值轉換爲固定點值,您仍然可以獲得小數精度,並且轉換爲字符串就像添加單個移位函數的整數一樣容易。

另一個想法是推出自己的snprintf()函數。從double到int的轉換本來就是由許多FPU單元支持的,所以它應該閃電般快速。將它轉換爲字符串也很簡單。

只是一些隨機的想法給你。

+1

謝謝Michael,哇,FPU支持這種轉換?這確實是一個好消息。另外分離零件並獨立打印它們的技巧很酷。謝謝這非常有幫助。 – antirez 2010-05-12 17:30:06

1

只要x在long long的範圍內,我沒有看到casts有問題。也許你應該檢查一下modf()函數,它將double分解爲其整數和小數部分。然後,您可以針對(double)LLONG_MIN和(double)LLONG_MAX添加檢查以確認整體部分。雖然雙精度可能會有困難。

但是在做任何事情之前,您是否確定它實際上是衡量其性能的瓶頸?整數值的百分比是否足夠高,以至於真的會有所作爲?

+2

非常感謝你,這已經實現,並導致保存數據庫與許多雙打兩倍的速度。在snprintf()函數顯示非常慢的分析會話之後開始優化... – antirez 2010-05-12 17:28:37

2

這樣做的問題是比較不會按照您期望的方式進行。僅僅因爲一個浮點值小於另一個浮點值並不意味着它作爲整數的表示將小於另一個。另外,我看到你比較(先前)平等的一個雙重價值之一。由於低位位的四捨五入和表示錯誤,您幾乎永遠不會想要做到這一點。

如果您只是在尋找某種類型的密鑰來做類似哈希的事情,那麼它可能會工作得很好。如果你真的關心哪些價值真的具有更大或更小的價值,那它就是一個壞主意。

+0

是的,我注意到了平等的雙重比較。它可能是令人討厭的來源,很難找到問題。它會在100次中使用99次。 – 2010-05-12 17:36:39

+1

你好泰德,如果你檢查代碼,我總是比較雙打,但是在兩步投射後會得到兩倍。所以這個想法是,如果雙精度匹配,那麼它就能夠通過這個「過濾器」而不會丟失信息。所以它的長表示可以被打印而不是它本身。 所以這樣做的原因只是爲了在雙字串轉換階段獲得速度。 – antirez 2010-05-12 17:40:20

+0

閱讀有關比較雙打,即使數字是完全相同的表示方式,這不起作用嗎? 我知道,如果兩個數字是從字符串表示或其他數學處理生成的,那麼比較可能會失敗,而數字仍然是epsilon-wise相同,但在我的具體情況下,我可以得到沒有問題的假陰性,因爲我會訴諸使用snprintf()的合理代碼。 如果我正確理解問題,比較雙打的問題是錯誤的否定,而不是誤報。 – antirez 2010-05-12 17:57:16

0

你的測試是完全正常的(假設你已經分別處理了無窮大和NANs) - 這可能是你想要比較浮點數是否相等的極少數occaisions之一。它不會調用未定義的行爲 - 即使x不在long long範圍之外,您只會得到一個「實現定義的結果」,這裏沒關係。

只有在美中不足的是,負零將結束爲正零(因爲負零比較等於正零)。

+0

感謝caf,我在最近幾小時研究了一些浮點數的表示。我也認爲這是安全的。是的,在代碼中已經明確檢查了Nan和Infinity,所以應該是安全的。 爲了讓事情看起來更安全一些,我添加了#if來檢查螳螂和長時間的精度匹配,所以只有在double有至少52位精度的情況下才會編譯代碼,然後按順序進行顯式測試檢查double是否在long long不會溢出的範圍內(並且此測試在52位範圍內完成,因此我們保證它可以正常工作)。 Thx回覆。 – antirez 2010-05-13 09:09:58

+1

範圍測試是不必要的 - 如果你願意,你甚至可以用'char'來代替。當'double'超出範圍時,您得到的實現定義的結果只是在轉換回'double'(C中的「溢出」僅發生在計算上,而不是轉換)時不會相等。 – caf 2010-05-13 21:37:24