क्या 0 के लिए समानता के लिए फ्लोटिंग पॉइंट वैल्यू की जाँच करना सुरक्षित है?


100

मुझे पता है कि आप सामान्य रूप से दोहरे या दशमलव प्रकार के मूल्यों के बीच समानता पर भरोसा नहीं कर सकते, लेकिन मैं सोच रहा था कि क्या 0 एक विशेष मामला है।

हालांकि मैं 0.00000000000001 और 0.00000000000002 के बीच की गड़बड़ियों को समझ सकता हूं, जबकि खुद को कुछ भी गड़बड़ करने के लिए बहुत मुश्किल लगता है। यदि आप कुछ भी नहीं कर रहे हैं, यह अब और कुछ नहीं है।

लेकिन मैं इस विषय के बारे में ज्यादा नहीं जानता, इसलिए यह कहना मेरे लिए नहीं है।

double x = 0.0;
return (x == 0.0) ? true : false;

क्या वह हमेशा सच लौटेगा?


69
टर्नरी ऑपरेटर उस कोड में बेमानी है :)
जोएल कोएहॉर्न

5
योग्य तुम सही हो। मेरे पास जाओ
जेन रॉबर्ट्स

मैं ऐसा नहीं करूंगा क्योंकि आप नहीं जानते हैं कि x कैसे शून्य पर सेट हो गया। यदि आप अभी भी इसे करना चाहते हैं, तो आप शायद 1e-12 से छुटकारा पाने के लिए राउंड या फ्लोर एक्स या ऐसा करना चाहते हैं, जिसे अंत में टैग किया जा सके।
रेक्स लोगन

जवाबों:


115

यह उम्मीद करना सुरक्षित है कि तुलना trueकेवल और अगर डबल चर का मान है 0.0(जो कि आपके मूल कोड स्निपेट में है, तो निश्चित रूप से, मामला है)। यह ==ऑपरेटर के शब्दार्थ के अनुरूप है । a == bका अर्थ है " aके बराबर हैb "।

यह सुरक्षित नहीं है (क्योंकि यह सही नहीं है ) यह आशा करना कि जब भी शुद्ध गणित में समान गणना का परिणाम शून्य होता है, तो कुछ गणना का परिणाम दोहरे (या अधिक सामान्यतः, अस्थायी बिंदु) अंकगणित में शून्य होगा। ऐसा इसलिए है क्योंकि जब गणना जमीन में आती है, तो फ्लोटिंग पॉइंट सटीक त्रुटि दिखाई देती है - एक अवधारणा जो गणित में रियल नंबर अंकगणित में मौजूद नहीं है।


51

यदि आपको बहुत अधिक "समानता" की तुलना करने की आवश्यकता है, तो तुलना करने के लिए .NET 3.5 में थोड़ा सहायक फ़ंक्शन या विस्तार विधि लिखना एक अच्छा विचार हो सकता है:

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

यह निम्नलिखित तरीके से इस्तेमाल किया जा सकता है:

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);

4
यदि आप इन संख्याओं के एक-दूसरे के बहुत अधिक मान हैं, तो डबल और डबल 2 की तुलना करके आपको एक घटाव रद्द करने की त्रुटि हो सकती है। मैं Math.Abs ​​को हटा दूंगा और प्रत्येक शाखा को व्यक्तिगत रूप से d1> = d2 - e और d1 <= d2 + e
Theodore Zographos

"क्योंकि एप्सिलॉन एक सकारात्मक मूल्य की न्यूनतम अभिव्यक्ति को परिभाषित करता है जिसकी सीमा शून्य के पास है, दो समान मूल्यों के बीच अंतर का अंतर एप्सिलॉन से अधिक होना चाहिए। आमतौर पर, यह एप्सिलॉन से कई गुना अधिक है। इस वजह से, हम अनुशंसा करते हैं कि आप ऐसा करें। समानता के लिए दोहरे मूल्यों की तुलना करते समय एप्सिलॉन का उपयोग न करें। " - msdn.microsoft.com/en-gb/library/ya2zha7s(v=vs.110).aspx
राफेल कोस्टा

15

आपके सरल नमूने के लिए, वह परीक्षण ठीक है। लेकिन इससे क्या:

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

याद रखें कि .1 बाइनरी में एक दोहराए जाने वाला दशमलव है और इसे बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है। फिर इस कोड की तुलना करें:

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

मैं आपको वास्तविक परिणाम देखने के लिए एक परीक्षण चलाने के लिए छोड़ दूँगा: आप इसे इस तरह याद रखने की अधिक संभावना रखते हैं।


5
दरअसल, यह किसी कारण से (LINQPad, कम से कम) के लिए सही है।
एलेक्सी रोमानोव

आप किस ".1 मुद्दे" की बात करते हैं?
तीजय

14

डबल.ईक्यूएल के लिए MSDN प्रविष्टि से :

तुलना में सटीक

समान पद्धति का उपयोग सावधानी के साथ किया जाना चाहिए, क्योंकि दो स्पष्ट रूप से समकक्ष मूल्य दो मूल्यों की भिन्नता के कारण असमान हो सकते हैं। निम्न उदाहरण बताता है कि डबल मान .3333 और डबल 3 से 1 को विभाजित करके लौटाए गए असमान हैं।

...

समानता के लिए तुलना करने के बजाय, एक अनुशंसित तकनीक में दो मूल्यों के बीच अंतर के स्वीकार्य मार्जिन को परिभाषित करना शामिल है (जैसे कि मूल्यों में से .01%)। यदि दोनों मानों के बीच के अंतर का पूर्ण मान उस मार्जिन से कम या बराबर है, तो अंतर सटीक और अंतर के कारण होने की संभावना है, इसलिए, मान समान होने की संभावना है। निम्नलिखित उदाहरण .33333 और 1/3 की तुलना करने के लिए इस तकनीक का उपयोग करता है, दो दोहरे मान जो पिछले कोड उदाहरण असमान पाए गए।

इसके अलावा, डबल देखें । एप्सिलॉन


1
यह बराबर के बराबर-बराबर मूल्यों के लिए भी संभव है। एक उम्मीद करेगा कि अगर x.Equals(y), तब (1/x).Equals(1/y), लेकिन अगर xऐसा है 0और yऐसा नहीं है 1/Double.NegativeInfinity। वे मूल्य समान घोषित करते हैं, भले ही उनके पारस्परिक न हों।
सुपरकाट

@ सुपरकैट: वे बराबर हैं। और वे पारस्परिक नहीं है। आप के साथ x = 0और फिर से अपना परीक्षण चला सकते हैं y = 0, और आप अभी भी पाएंगे 1/x != 1/y
बेन वोइगट

@BenVoigt: के साथ xऔर yप्रकार के रूप में double? आप रिपोर्ट को असमान बनाने के लिए परिणामों की तुलना कैसे करते हैं? ध्यान दें कि 1 / 0.0 NaN नहीं है।
सुपरकैट

@ सुपरकैट: ठीक है, यह उन चीजों में से एक है जो IEEE-754 गलत हो जाता है। (पहला, जो 1.0/0.0NaN होने में विफल रहता है जैसा कि होना चाहिए, जैसा कि यह सीमा अद्वितीय नहीं है। दूसरी बात, कि अनंतता की डिग्री पर कोई ध्यान न देते हुए, शिशु एक दूसरे के बराबर तुलना करते हैं)
Ben Voigt

@BenVoigt: यदि शून्य दो बहुत छोटी संख्याओं को गुणा करने का परिणाम था, तो 1.0 को उस में विभाजित करना चाहिए, जिसमें किसी भी संख्या की तुलना में छोटी संख्याओं की तुलना में अधिक समान चिह्न हो, और यदि किसी संख्या में से कम संख्या हो तो संख्याओं के विपरीत संकेत थे। IMHO, IEEE-754 बेहतर होगा यदि इसमें अहस्ताक्षरित शून्य था, लेकिन सकारात्मक और नकारात्मक infinitesimals।
सुपरकैट

6

समस्या तब आती है जब आप विभिन्न प्रकार के फ्लोटिंग पॉइंट वैल्यू कार्यान्वयन की तुलना करते हैं, जैसे फ्लोट की तुलना डबल के साथ। लेकिन एक ही प्रकार के साथ, यह एक समस्या नहीं होनी चाहिए।

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

समस्या यह है, प्रोग्रामर कभी-कभी यह भूल जाता है कि तुलना के लिए निहित प्रकार कास्ट (डबल फ्लोट) हो रहा है और इसका परिणाम बग में होता है।


3

यदि संख्या को सीधे फ्लोट या डबल को सौंपा गया था, तो शून्य या किसी भी पूरी संख्या के खिलाफ परीक्षण करना सुरक्षित है जिसे एक फ्लोट के लिए डबल या 24 बिट्स के लिए 53 बिट्स में दर्शाया जा सकता है।

या इसे दूसरे तरीके से रखने के लिए आप हमेशा डबल को पूर्णांक और पूर्णांक मान दे सकते हैं और फिर डबल को एक ही पूर्णांक से तुलना कर सकते हैं और गारंटी दी जाएगी कि यह समान होगा।

आप पूरी संख्या निर्दिष्ट करके भी शुरुआत कर सकते हैं और सरल तुलनाएँ जोड़कर, घटाकर या गुणा करके पूरी संख्या में काम करना जारी रखते हैं (यह मानते हुए कि परिणाम डबल के लिए फ्लोट पेट 53 बिट्स के लिए 24 बिट्स से कम है)। तो आप कुछ नियंत्रित स्थितियों के तहत फ़्लोटर्स और डबल्स को पूर्णांकों के रूप में मान सकते हैं।


मैं सामान्य रूप से आपके कथन से सहमत हूं (और इसे अपडाउन किया) लेकिन मेरा मानना ​​है कि यह वास्तव में निर्भर करता है कि IEEE 754 फ़्लोटिंग पॉइंट कार्यान्वयन का उपयोग किया जाता है या नहीं। और मेरा मानना ​​है कि प्रत्येक "आधुनिक" कंप्यूटर IEEE 754 का उपयोग करता है, कम से कम फ़्लोट्स के भंडारण के लिए (इसमें अजीब गोलाई के नियम हैं जो भिन्न होते हैं)।
निशान लता

2

नहीं, यह ठीक नहीं है। 0.0 के बराबर की तुलना में तथाकथित असामान्य मान (सबनॉर्मल), झूठ (गैर-शून्य) के रूप में तुलना करेंगे, लेकिन जब एक समीकरण में उपयोग किया जाता है तो इसे सामान्यीकृत किया जाएगा (0.0)। इस प्रकार, विभाजन से बचने के लिए एक तंत्र के रूप में इसका उपयोग करना सुरक्षित नहीं है। इसके बजाय, 1.0 जोड़ें और 1.0 की तुलना करें। यह सुनिश्चित करेगा कि सभी सबमॉर्नल्स को शून्य माना जाए।


Subnormals भी रूप में जाना जाता denormals
मैनुअल

जब उपयोग किया जाता है तो सबनॉर्मल शून्य के बराबर नहीं होते हैं, हालांकि वे सटीक ऑपरेशन के आधार पर एक ही परिणाम का उत्पादन कर सकते हैं या नहीं कर सकते हैं।
wnoise

-2

यह कोशिश करो, और आप पाएंगे कि == डबल / फ्लोट के लिए विश्वसनीय नहीं है।
double d = 0.1 + 0.2; bool b = d == 0.3;

यहां क्वोरा का जवाब है।


-4

वास्तव में, मुझे लगता है कि 0.0 के मुकाबले दोहरे मान की तुलना करने के लिए निम्नलिखित कोड का उपयोग करना बेहतर है:

double x = 0.0;
return (Math.Abs(x) < double.Epsilon) ? true : false;

फ्लोट के लिए भी:

float x = 0.0f;
return (Math.Abs(x) < float.Epsilon) ? true : false;

5
डॉक्स से डबल से। ईप्सिलॉन: "यदि आप एक कस्टम एल्गोरिथ्म बनाते हैं जो यह निर्धारित करता है कि क्या दो फ़्लोटिंग-पॉइंट नंबर को समान माना जा सकता है, तो आपको अंतर के स्वीकार्य पूर्ण मार्जिन को स्थापित करने के लिए एप्सिलॉन निरंतर से अधिक मूल्य का उपयोग करना होगा दोनों मानों को समान माना जाता है। (आमतौर पर, अंतर का वह अंश एप्सिलॉन से कई गुना अधिक होता है।) "
एलेस्टेयर मोव

1
@AlastairMaw यह समानता के लिए किसी भी आकार के दो डबल्स की जाँच करने के लिए लागू होता है। शून्य के लिए समानता की जाँच के लिए, डबल। एप्सिलॉन ठीक है।
13

4
नहीं, यह नहीं है । यह बहुत संभावना है कि आप कुछ गणना के माध्यम से जिस मूल्य पर पहुंचे हैं वह शून्य से कई गुना एप्सिलॉन है, लेकिन फिर भी इसे शून्य माना जाना चाहिए। आप जादुई रूप से कहीं से भी अपने मध्यवर्ती परिणाम में अतिरिक्त परिशुद्धता का एक पूरा गुच्छा प्राप्त नहीं करते हैं, सिर्फ इसलिए कि यह शून्य के करीब होता है।
एलेस्टेयर माव

4
उदाहरण के लिए: (1.0 / 5.0 + 1.0 / 5.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0) <double.Epsilon == असत्य (और बहुत अधिक परिमाण में): 2.78E-17 बनाम 4.94E -324)
एलेस्टेयर माव

तो, क्या सटीक की सिफारिश की है, अगर double.Epsilon ठीक नहीं है? Epsilon ठीक 10 बार होगा? 100 बार?
लियांग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.