2010-05-27 65 views
9

給定兩個浮點數,我在尋找高效的方法來檢查它們是否具有相同的符號的兩個值爲零(+0.0或-0.0),則應該認爲它們具有相同的符號如何在處理負零時有效比較兩個浮點值的符號

例如,

  • SameSign(1.0,2.0)應返回true
  • SameSign(-1.0,-2.0)應返回true
  • SameSign(-1.0,2.0)應該返回false
  • SameSign(0.0,1.0)應返回true
  • SameSign(0.0,-1.0)應返回true
  • SameSign(-0.0,1.0)應返回true
  • SameSign(-0.0,-1.0)應返回true

C++中的幼稚,但正確實施SameSign是:

bool SameSign(float a, float b) 
{ 
    if (fabs(a) == 0.0f || fabs(b) == 0.0f) 
     return true; 

    return (a >= 0.0f) == (b >= 0.0f); 
} 

假設IEEE浮點模型,這裏的編譯爲網點代碼的SameSign的變體(至少用Visual C++ 2008):

bool SameSign(float a, float b) 
{ 
    int ia = binary_cast<int>(a); 
    int ib = binary_cast<int>(b); 

    int az = (ia & 0x7FFFFFFF) == 0; 
    int bz = (ib & 0x7FFFFFFF) == 0; 
    int ab = (ia^ib) >= 0; 

    return (az | bz | ab) != 0; 
} 

binary_cast定義如下:

template <typename Target, typename Source> 
inline Target binary_cast(Source s) 
{ 
    union 
    { 
     Source m_source; 
     Target m_target; 
    } u; 
    u.m_source = s; 
    return u.m_target; 
} 

我在尋找兩件事情:

  1. 更快,更有效的實現的SameSign,使用位技巧,FPU技巧甚至SSE內在因素。

  2. SameSign有效延伸至三個值

編輯:

我做了對的SameSign的三種變體(在原來的問題描述的兩個變種,加上斯蒂芬的一個)的一些性能測量。每個函數運行200-400次,在101個浮點數的所有連續值對中隨機填充-1.0,-0.0,+0.0和+1.0。每次測量重複2000次,並保持最短時間(以清除所有緩存效應和系統誘導的減速)。該代碼是使用Visual C++ 2008 SP1進行編譯的,並且已啓用最大化優化和SSE2代碼生成。測量是在Core 2 Duo P8600 2.4 Ghz上完成的。

這裏有定時,不計算從陣列獲取的輸入值,調用函數和檢索結果(其量以6-7 clockticks不等)的開銷:

  • 樸素變體:15蜱
  • 位魔術變型:13蜱
  • 斯蒂芬斯的變體:6蜱
+0

任何特定的語言/平臺? – 2010-05-27 15:50:43

+0

嘿,謝謝你的好問題:)最好是x86上的C/C++。 – 2010-05-27 15:54:01

+0

[比較兩個浮點數以查看它們是否都是負數,或者兩個都是正數]可能有重複。(http://stackoverflow.com/questions/2013680/comparing-two-floats-to-see-if-theyre-both -negative-or-both-positive) – ChrisF 2011-07-12 15:05:33

回答

10

如果您不需要支持無窮大,可以Ĵ ust使用:

inline bool SameSign(float a, float b) { 
    return a*b >= 0.0f; 
} 

這在大多數現代硬件上確實非常快,而且是完全便攜的。然而,它在(零,無窮大)情況下無法正常工作,因爲零*無窮是NaN,無論符號如何,比較都將返回false。當a和b都很小時,它也會在某些硬件上產生一個反常的失速。

+0

事實上,這對兩個值運作良好,並具有適當的語義。我唯一擔心的是它需要對三個值的情況進行三次乘法運算(a * b> = 0.0f && a * c> = 0.0f && b * c> = 0.0f)。 – 2010-05-27 16:54:13

+0

@弗朗索瓦:是的,三值的情況是一個有趣的謎題。我將不得不考慮一點。 – 2010-05-27 17:06:11

+0

這是確切的嗎?對我來說,這將是明顯的解決方案,但我也需要得到一個精確的結果,而不管舍入錯誤。在我看來,a * b可能向上舍入到0,然後該函數計算錯誤的值。不知道,但。 – migle 2013-09-03 10:35:29

3

也許是這樣的:

inline bool same_sign(float a, float b) { 
    return copysignf(a,b) == a; 
} 

看到複製符號的手冊頁瞭解更多信息於它(也可能要檢查-0 = + 0!)

或者可能這如果你有C99功能

inline bool same_sign(float a, float b) { 
    return signbitf(a) == signbitf(b); 
} 

一個側面說明,在GCC至少兩個複製符號和signbit的內置函數,使他們要快,如果你想確保正在使用的內置版本在signbit這也應該是很容易延伸到3的值的情況下,以及(實際上這兩個應該...)

inline bool same_sign(float a, float b, float c) { 
    return copysignf(a,b) == a && copysignf(a,c) == a; 
} 

// trust the compiler to do common sub-expression elimination 
inline bool same_sign(float a, float b, float c) { 
    return signbitf(a) == signbitf(b) && signbitf(a) == signbitf(c); 
} 

// the manpages do not say that signbit returns 1 for negative... however 
// if it does this should be good, (no branches for one thing...) 
inline bool same_sign(float a, float b, float c) { 
    int s = signbitf(a) + signbitf(b) + signbitf(c); 
    return !s || s==3; 
} 
0

小記:你可以做__builtin_signbitf(一)

編輯:宏返回一個int,手冊頁聲明「如果x的值設置了符號位,它將返回一個非零值。」這意味着Spudd86的不保證能夠在signbit返回兩個不同的非零整數的情況下用於兩個不同的負值。

鑄造第一爲bool確保正確的返回值:

inline bool same_sign(float a, float b) { 
    return (bool)signbitf(a) == (bool)signbitf(b); 
}