2010-05-27 38 views
9

Cho hai số dấu phẩy động, tôi đang tìm cách cách hiệu quả để kiểm tra xem chúng có cùng dấu hiệu không, . của hai giá trị bằng không (+0.0 hoặc -0.0), chúng phải được xem là có cùng một dấu hiệu.Cách so sánh hiệu quả ký hiệu của hai giá trị dấu phẩy động trong khi xử lý các giá trị dấu phẩy động

Ví dụ,

  • SameSign (1.0, 2.0) nên trở về đúng
  • SameSign (-1.0, -2.0) nên trở về đúng
  • SameSign (-1.0, 2.0) nên trả về false
  • SameSign (0.0, 1.0) nên trở về đúng
  • SameSign (0.0, -1.0) nên trở về đúng
  • SameSign (-0,0, 1,0) sẽ trả về đúng
  • SameSign (-0,0, -1,0) nên trở về đúng

Một thực hiện ngây thơ nhưng đúng SameSign trong C++ sẽ là:

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

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

Giả sử mô hình điểm nổi IEEE, đây là biến thể của SameSign biên dịch thành mã không có nhánh (ít nhất là với 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; 
} 

với binary_cast được xác định như sau:

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; 
} 

Tôi đang tìm kiếm hai điều:

  1. Một thực hiện nhanh hơn, hiệu quả hơn SameSign, sử dụng thủ đoạn chút, FPU thủ đoạn hoặc thậm chí là bản chất của SSE.

  2. Mở rộng hiệu quả SameSign thành ba giá trị.

Edit:

Tôi đã thực hiện một số phép đo hiệu suất trên ba biến thể của SameSign (hai biến thể được mô tả trong câu hỏi ban đầu, cộng với Stephen một). Mỗi hàm được chạy 200-400 lần, trên tất cả các cặp giá trị liên tiếp trong một mảng gồm 101 float được điền một cách ngẫu nhiên với -1.0, -0.0, +0.0 và +1.0. Mỗi phép đo được lặp lại 2000 lần và thời gian tối thiểu được giữ lại (để loại bỏ tất cả các hiệu ứng bộ nhớ đệm và sự chậm lại do hệ thống gây ra). Mã được biên dịch với Visual C++ 2008 SP1 với tối ưu hóa tối đa và tạo mã SSE2 được kích hoạt. Các phép đo được thực hiện trên Core 2 Duo P8600 2.4 Ghz.

Sau đây là các timings, không kể những phí của lấy giá trị đầu vào từ mảng, gọi hàm và lấy kết quả (trong đó số tiền 6-7 clockticks):

  • biến Naive: 15 ticks
  • Bit biến ma thuật: 13 ticks
  • biến Stephens của: 6 ticks
+0

Bất kỳ đặc biệt ngôn ngữ/nền tảng? –

+0

Xin chào, cảm ơn câu hỏi hay :) Tốt hơn là C/C++ trên x86. –

+0

có thể trùng lặp của [so sánh hai phao nổi để xem chúng có phải là số âm hay cả hai đều dương.] (Http://stackoverflow.com/questions/2013680/comparing-two-floats-to-see-if-theyre-both -Negative-hoặc-cả hai-tích cực) – ChrisF

Trả lời

10

Nếu bạn không cần phải hỗ trợ infinities, bạn có thể j sử dụng ust:

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

thực sự khá nhanh trên phần cứng hiện đại nhất và hoàn toàn di động. Nó không hoạt động đúng trong trường hợp (không, vô cùng) tuy nhiên, vì không * vô cùng là NaN, và so sánh sẽ trả về false, bất kể các dấu hiệu. Nó cũng sẽ phải chịu một gian hàng không bình thường trên một số phần cứng khi a và b đều nhỏ.

+0

Thật vậy, điều này hoạt động tốt cho hai giá trị, và có ngữ nghĩa thích hợp. Mối quan tâm duy nhất của tôi là nó đòi hỏi ba phép nhân cho ba trường hợp giá trị (a * b> = 0.0f && a * c> = 0.0f && b * c> = 0.0f). –

+0

@ François: vâng, trường hợp ba giá trị là một câu đố thú vị. Tôi sẽ phải suy nghĩ về nó một chút. –

+0

Đây có phải là chính xác không? Với tôi, đây sẽ là giải pháp hiển nhiên, nhưng tôi cũng cần có kết quả chính xác bất kể lỗi làm tròn. Nó xuất hiện với tôi rằng một * b có thể được làm tròn lên trên về phía 0 và sau đó chức năng này tính toán giá trị sai. Không chắc chắn mặc dù. – migle

3

có lẽ cái gì đó như:

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

thấy man page của copysign để biết thêm về những gì nó (cũng có thể bạn muốn kiểm tra xem -0 = 0!)

hoặc có thể đây nếu bạn có C99 chức năng

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

như một mặt lưu ý, trên gcc ít nhất cả copysign và signbit được chức năng được xây dựng trong để họ nên được nhanh chóng, nếu bạn muốn chắc chắn rằng phiên bản dựng sẵn đang được sử dụng bạn có thể làm __builtin_signbitf (a)

EDIT: điều này cũng nên dễ mở rộng đến các trường hợp 3 giá trị cũng như (thực sự cả hai nên ...)

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

Một lưu ý nhỏ trên signbit: Macro trả về một trang int và trang người dùng nói rằng "Nó trả về một giá trị khác nếu giá trị của x có bit dấu của nó." Điều này có nghĩa là số của Spudd86 không được đảm bảo để hoạt động trong trường hợp ký hiệu trả về hai số không khác nhau của int cho hai giá trị âm khác nhau.

Đúc đến bool đầu tiên đảm bảo một giá trị trả về đúng:

inline bool same_sign(float a, float b) { 
    return (bool)signbitf(a) == (bool)signbitf(b); 
} 
Các vấn đề liên quan