टी एल; डॉ
- संभावित रूप से अधिक कुशल होते हुए, कुछ सीमा मामलों में कुछ अवांछनीय परिणामों से बचने के लिए वर्तमान में स्वीकृत समाधान के बजाय निम्नलिखित फ़ंक्शन का उपयोग करें।
- अपने संख्याओं पर अपेक्षित अपव्यय को जानें और तुलनात्मक कार्य के अनुसार उन्हें खिलाएं।
bool nearly_equal(
float a, float b,
float epsilon = 128 * FLT_EPSILON, float relth = FLT_MIN)
{
assert(std::numeric_limits<float>::epsilon() <= epsilon);
assert(epsilon < 1.f);
if (a == b) return true;
auto diff = std::abs(a-b);
auto norm = std::min((std::abs(a) + std::abs(b)), std::numeric_limits<float>::max());
return diff < std::max(relth, epsilon * norm);
}
ग्राफिक्स, कृपया?
फ्लोटिंग पॉइंट नंबरों की तुलना करते समय, दो "मोड" होते हैं।
पहला एक सापेक्ष मोड है, जहां उनके आयाम के बीच का अंतर x
और y
अपेक्षाकृत माना जाता है |x| + |y|
। जब 2 डी में प्लॉट किया जाता है, तो यह निम्नलिखित प्रोफ़ाइल देता है, जहां हरे रंग का अर्थ है समानता x
और y
। (मैंने epsilon
चित्रण प्रयोजनों के लिए 0.5 लिया )।
सापेक्ष मोड वह है जो "सामान्य" या "बड़े पर्याप्त" फ्लोटिंग पॉइंट मान के लिए उपयोग किया जाता है। (उस पर और बाद में)।
दूसरा एक पूर्ण विधा है, जब हम उनके अंतर की निश्चित संख्या से तुलना करते हैं। यह निम्नलिखित प्रोफाइल देता है ( चित्रण के लिए फिर epsilon
से 0.5 और relth
1 के साथ)।
तुलना का यह पूर्ण विधा है, जो "छोटे" फ्लोटिंग पॉइंट मानों के लिए उपयोग किया जाता है।
अब सवाल यह है कि हम उन दो प्रतिक्रिया पैटर्न को एक साथ कैसे जोड़ते हैं।
माइकल बोर्गवर्ड के उत्तर में, स्विच मूल्य पर आधारित है diff
, जो नीचे relth
( Float.MIN_NORMAL
उसके उत्तर में) होना चाहिए । इस स्विच ज़ोन को नीचे दिए गए ग्राफ़ में रचा गया दिखाया गया है।
क्योंकि relth * epsilon
छोटा होता है कि relth
, हरी पैच एक साथ छड़ी नहीं है, बारी में समाधान एक बुरा संपत्ति देता है: हम संख्या ऐसी है कि के साथ तीन बच्चों पा सकते हैं x < y_1 < y_2
और अभी तक x == y2
लेकिन x != y1
।
इसका उदाहरण लें:
x = 4.9303807e-32
y1 = 4.930381e-32
y2 = 4.9309825e-32
हमारे पास है x < y1 < y2
, और वास्तव y2 - x
में 2000 गुना से अधिक बड़ा है y1 - x
। और फिर भी वर्तमान समाधान के साथ,
nearlyEqual(x, y1, 1e-4) == False
nearlyEqual(x, y2, 1e-4) == True
इसके विपरीत, ऊपर प्रस्तावित समाधान में, स्विच ज़ोन के मूल्य पर आधारित है |x| + |y|
, जिसे नीचे दिए गए हैट स्क्वायर द्वारा दर्शाया गया है। यह सुनिश्चित करता है कि दोनों क्षेत्र इनायत से जुड़ते हैं।
इसके अलावा, ऊपर दिए गए कोड में ब्रांचिंग नहीं है, जो अधिक कुशल हो सकता है। उस ऑपरेशन पर विचार करें जैसे कि , max
और abs
, जिसे प्राथमिकताओं में शाखाओं की आवश्यकता होती है, अक्सर विधानसभा निर्देश समर्पित होते हैं। इस कारण से, मुझे लगता है कि इस दृष्टिकोण एक और समाधान है कि माइकल के ठीक करने के लिए किया जाएगा से बेहतर है nearlyEqual
से स्विच को बदलने के द्वारा diff < relth
करने के लिए diff < eps * relth
है, जो तब मूलतः एक ही प्रतिक्रिया पैटर्न का उत्पादन होगा।
रिश्तेदार और पूर्ण तुलना के बीच स्विच कहां करें?
उन मोड के बीच का स्विच चारों ओर बना होता है relth
, जिसे FLT_MIN
स्वीकृत उत्तर के रूप में लिया जाता है। इस विकल्प का मतलब है कि वह float32
है जो हमारे फ्लोटिंग पॉइंट नंबरों की शुद्धता को सीमित करता है।
यह हमेशा समझ में नहीं आता है। उदाहरण के लिए, यदि आप जिन संख्याओं की तुलना करते हैं, वे घटाव के परिणाम हैं, शायद कुछ की सीमा में FLT_EPSILON
अधिक समझ में आता है। यदि वे घटाए गए संख्याओं की जड़ें हैं, तो संख्यात्मक संसेचन और भी अधिक हो सकता है।
जब आप एक फ्लोटिंग पॉइंट की तुलना करने पर विचार करते हैं तो यह स्पष्ट है 0
। यहां, किसी भी सापेक्ष तुलना विफल हो जाएगी, क्योंकि |x - 0| / (|x| + 0) = 1
। इसलिए तुलना को पूर्ण रूप से बदलने की जरूरत है जब x
आपकी गणना की असंभवता के क्रम पर है - और शायद ही कभी यह जितना कम हो FLT_MIN
।
यह relth
ऊपर दिए गए पैरामीटर की शुरूआत का कारण है।
इसके अलावा, इसके relth
साथ गुणा नहीं करने से epsilon
, इस पैरामीटर की व्याख्या सरल है और संख्यात्मक सटीकता के स्तर के अनुरूप है जो हम उन नंबरों पर उम्मीद करते हैं।
गणितीय रूंबिंग
(यहां ज्यादातर अपनी मर्जी से रखा गया)
आमतौर पर मैं मानता हूं कि एक अच्छी तरह से व्यवहार किया गया फ्लोटिंग पॉइंट तुलना ऑपरेटर =~
में कुछ बुनियादी गुण होने चाहिए।
निम्नलिखित स्पष्ट नहीं हैं:
- आत्म-समानता:
a =~ a
- समरूपता:
a =~ b
तात्पर्य हैb =~ a
- विपक्ष द्वारा आक्रमण:
a =~ b
तात्पर्य-a =~ -b
(हमारे पास नहीं है a =~ b
और b =~ c
इसका मतलब है a =~ c
, =~
एक समानता संबंध नहीं है)।
मैं निम्नलिखित गुण जोड़ूंगा जो फ्लोटिंग पॉइंट तुलनाओं के लिए अधिक विशिष्ट हैं
- यदि
a < b < c
, तो a =~ c
तात्पर्य a =~ b
(निकट मान भी समान होना चाहिए)
- यदि
a, b, m >= 0
तब a =~ b
तात्पर्य है a + m =~ b + m
(समान अंतर के साथ बड़े मान भी बराबर होने चाहिए)
- अगर
0 <= λ < 1
तब a =~ b
तात्पर्य है λa =~ λb
(शायद तर्क के लिए कम स्पष्ट)।
वे गुण पहले से ही सम-विषम कार्यों के लिए मजबूत अवरोध प्रदान करते हैं। ऊपर प्रस्तावित कार्य उन्हें सत्यापित करता है। शायद एक या कई अन्यथा स्पष्ट गुण गायब हैं।
जब एक के बारे में सोच =~
के रूप में समानता के रिश्ते की एक परिवार =~[Ɛ,t]
द्वारा parameterized Ɛ
और relth
, एक भी जोड़ सकता है
- यदि
Ɛ1 < Ɛ2
फिर a =~[Ɛ1,t] b
इसका मतलब है a =~[Ɛ2,t] b
(किसी दिए गए सहिष्णुता के लिए समानता का अर्थ है उच्च सहिष्णुता में समानता)
- अगर
t1 < t2
तब a =~[Ɛ,t1] b
तात्पर्य है a =~[Ɛ,t2] b
(किसी दिए गए अभेद्यता के लिए समानता का अर्थ है एक उच्च वृद्धि पर समानता)
प्रस्तावित समाधान भी इनकी पुष्टि करता है।