फ्लोटिंग पॉइंट राउंडिंग त्रुटियों के समाधान


18

कई गणितीय गणनाओं से संबंधित एक एप्लिकेशन के निर्माण में, मैंने इस समस्या का सामना किया है कि कुछ संख्याएं गोल त्रुटियों का कारण बनती हैं।

जब मैं समझता हूं कि फ्लोटिंग पॉइंट सटीक नहीं है , तो समस्या यह है कि मैं यह सुनिश्चित करने के लिए सटीक संख्याओं के साथ कैसे निपटता हूं कि जब गणना उन पर पूर्ववर्ती हो तो फ्लोटिंग पॉइंट किसी भी मुद्दे का कारण नहीं बनता है?


2
क्या आपके सामने कोई विशिष्ट समस्या है? परीक्षण करने के कई तरीके हैं, सभी कुछ समस्या के लिए सही हैं। प्रश्न जिनके कई उत्तर हो सकते हैं वे प्रश्नोत्तर प्रारूप के लिए खराब हैं। यह सबसे अच्छा होगा यदि आप उस समस्या को परिभाषित कर सकते हैं जो आपके विचारों और सिफारिशों के लिए जाल डालने के बजाय एक सही उत्तर हो सकता है।

मैं बहुत से गणितीय योगों के साथ एक सॉफ्टवेयर एप्लीकेशन बना रहा हूं। मैं समझता हूं कि NUNIT या JUNIT परीक्षण अच्छा होगा, लेकिन गणितीय कैलकुलेशन के साथ मुद्दों पर दृष्टिकोण करने के बारे में एक विचार रखना पसंद करेंगे।
JNL

1
क्या आप एक गणना का उदाहरण दे सकते हैं जिसका आप परीक्षण कर रहे हैं? आम तौर पर कच्चे गणित का परीक्षण इकाई नहीं होगा (जब तक कि आप अपने स्वयं के संख्यात्मक प्रकारों का परीक्षण नहीं कर रहे हैं), लेकिन परीक्षण की तरह कुछ का distanceTraveled(startVel, duration, acceleration)परीक्षण किया जाएगा।

एक उदाहरण दशमलव बिंदुओं के साथ काम करेगा। उदाहरण के लिए, मान लें कि हम दूर x-0 से x = 14.589 के लिए विशेष सेटिंग्स के साथ एक दीवार का निर्माण कर रहे हैं और फिर x = 14.589 से x = दीवार के अंत तक कुछ व्यवस्थाएं। दूरी .589 जब बाइनरी में परिवर्तित होती है तो समान नहीं होती है .... विशेष रूप से अगर हम कुछ दूरी जोड़ते हैं ... जैसे 14.589 + 0.25 बाइनरी में 14.84 के बराबर नहीं होगी .... मुझे उम्मीद है कि यह भ्रमित नहीं होगा?
JNL

1
@MichaelT प्रश्न संपादित करने के लिए धन्यवाद। बहुत मदद की। चूंकि यह नया है, इसलिए प्रश्नों को फ्रेम करना बहुत अच्छा नहीं है। :) ... लेकिन जल्द ही अच्छा होगा।
JNL

जवाबों:


22

वैकल्पिक संख्यात्मक प्रकार बनाने के लिए तीन मौलिक दृष्टिकोण हैं जो फ्लोटिंग पॉइंट राउंडिंग से मुक्त हैं। इनके साथ सामान्य विषय यह है कि वे विभिन्न तरीकों से पूर्णांक गणित का उपयोग करते हैं।

परिमेय

एक अंश और एक भाजक के साथ पूरे भाग और तर्कसंगत संख्या के रूप में संख्या का प्रतिनिधित्व करते हैं। संख्या के 15.589रूप में प्रतिनिधित्व किया जाएगा w: 15; n: 589; d:1000

जब 0.25 (जो है w: 0; n: 1; d: 4) में जोड़ा जाता है , इसमें एलसीएम की गणना करना और फिर दो संख्याओं को जोड़ना शामिल है। यह कई परिस्थितियों के लिए अच्छी तरह से काम करता है, हालांकि परिणाम बहुत बड़ी संख्या में हो सकता है जब आप कई तर्कसंगत संख्याओं के साथ काम कर रहे होते हैं जो एक दूसरे के लिए अपेक्षाकृत प्रमुख होते हैं।

स्थिर केंद्र

आपके पास पूरा हिस्सा है, और दशमलव हिस्सा है। सभी संख्याएँ गोल होती हैं (उस शब्द में - लेकिन आप जानते हैं कि यह कहाँ है) उस परिशुद्धता के लिए। उदाहरण के लिए, आप 3 दशमलव बिंदुओं के साथ बिंदु तय कर सकते हैं। 15.589+ दशमलव भाग के लिए 0.250जोड़ 589 + 250 % 1000रहा है (और फिर पूरे भाग के लिए किसी भी ले)। यह मौजूदा डेटाबेस के साथ बहुत अच्छी तरह से काम करता है। जैसा कि उल्लेख किया गया है, गोलाई है लेकिन आप जानते हैं कि यह कहाँ है और इसे ऐसे निर्दिष्ट कर सकते हैं कि यह ज़रूरत से ज़्यादा सटीक है (आप केवल 3 दशमलव बिंदुओं को माप रहे हैं, इसलिए इसे 4 तय करें)।

फ़्लोटिंग निश्चित बिंदु

एक मान और सटीक स्टोर करें। 15.589के रूप में संग्रहीत किया जाता है 15589मूल्य के लिए और 3, परिशुद्धता के लिए है, जबकि 0.25के रूप में संग्रहीत किया जाता है 25और 2। यह मनमानी परिशुद्धता को संभाल सकता है। मेरा मानना ​​है कि यह जावा के बिगडेसिमल का उपयोग करता है (हाल ही में इस पर ध्यान नहीं दिया गया है) का उपयोग करता है। कुछ बिंदु पर, आप इसे इस प्रारूप से वापस लेना चाहेंगे और इसे प्रदर्शित करेंगे - और इसमें गोलाई शामिल हो सकती है (फिर से, आप इसे नियंत्रित करते हैं जहां यह है)।


एक बार जब आप प्रतिनिधित्व के लिए विकल्प निर्धारित करते हैं, तो आप या तो मौजूदा तीसरे पक्ष के पुस्तकालयों को पा सकते हैं जो इस का उपयोग करते हैं, या अपना खुद का लिखते हैं। अपना खुद का लेखन करते समय, इसका परीक्षण करना सुनिश्चित करें और सुनिश्चित करें कि आप गणित सही ढंग से कर रहे हैं।


2
यह एक अच्छी शुरुआत है, लेकिन निश्चित रूप से यह पूरी तरह से समस्या को हल नहीं करता है। √ , e और don't2 जैसी अपरिमेय संख्याओं में कड़ाई से संख्यात्मक प्रतिनिधित्व नहीं है; यदि आप एक सटीक प्रतिनिधित्व चाहते हैं, तो आप उन्हें प्रतीकात्मक रूप से प्रतिनिधित्व करने की आवश्यकता है, या यदि आप केवल गोल त्रुटि को कम करना चाहते हैं तो उनका यथासंभव मूल्यांकन करें।
कालेब

अपरिमेय के लिए @ कैलेब किसी भी गोलाई समस्या पैदा कर सकता है, जहां से परे करने के लिए उन्हें मूल्यांकन करने की आवश्यकता होगी। उदाहरण के लिए, 22/7 0.1% पाई पर सटीक है, 355/113 10 ^ -8 के लिए सटीक है। यदि आप केवल 3 दशमलव स्थानों की संख्या के साथ काम कर रहे हैं, तो 3.141592653 को 3 दशमलव स्थानों पर किसी भी गोल त्रुटियों से बचना चाहिए।

@MichaelT: परिमेय संख्याओं के अलावा आपको LCM को खोजने की आवश्यकता नहीं है और इसके बाद (और तेजी से "LSB शून्य" को रद्द करने की आवश्यकता नहीं है, और केवल तभी पूरी तरह से सरल हो सकता है जब आवश्यक हो)। सामान्य रूप से तर्कसंगत संख्याओं के लिए यह आम तौर पर केवल "अंश / भाजक" या "अंश / भाजक << घातांक" (और न कि "संपूर्ण भाग + अंश / भाजक") होता है। साथ ही आपका "फ्लोटिंग फिक्स्ड पॉइंट" एक फ्लोटिंग पॉइंट प्रतिनिधित्व है, और इसे "मनमाने आकार के फ्लोटिंग पॉइंट" के रूप में वर्णित किया जाएगा (इसे "निश्चित आकार के फ्लोटिंग पॉइंट से अलग करना")।
ब्रेंडन

आपकी कुछ शब्दावली थोड़ी सी iffy है - फ़्लोटिंग फिक्स्ड पॉइंट कोई मतलब नहीं है - मुझे लगता है कि आप फ़्लोटिंग दशमलव कहने की कोशिश कर रहे हैं।
जे.के.

10

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

अब सवाल यह है कि, "मैं फ्लोटिंग पॉइंट वेरिएबल्स के बिना गैर-पूर्णांक मानों को शामिल करते हुए गणित कैसे करूं?" उत्तर मनमाना-सटीक डेटा प्रकारों के साथ है । गणना धीमी है क्योंकि उन्हें हार्डवेयर के बजाय सॉफ़्टवेयर में लागू किया जाना है, लेकिन वे सटीक हैं। आपने यह नहीं कहा कि आप किस भाषा का उपयोग कर रहे हैं, इसलिए मैं पैकेज की सिफारिश नहीं कर सकता, लेकिन अधिकांश लोकप्रिय प्रोग्रामिंग भाषाओं के लिए मनमाना सटीक पुस्तकालय उपलब्ध हैं।


मैं अभी VC ++ का उपयोग कर रहा हूं ... लेकिन मैं अन्य प्रोग्रामिंग भाषाओं के संबंध में किसी भी अधिक जानकारी की सराहना करूंगा।
JNL

फ़्लोटिंग पॉइंट वैल्यू के बिना भी आप गोल समस्याओं में चलने वाले हैं।
चाद

2
@ सच है, लेकिन लक्ष्य गोल समस्याओं को समाप्त करना नहीं है (जो हमेशा मौजूद रहेगा, क्योंकि आपके द्वारा उपयोग किए जाने वाले किसी भी आधार में कुछ संख्याएं हैं जिनका कोई सटीक प्रतिनिधित्व नहीं है, और आपके पास अनंत स्मृति और प्रसंस्करण शक्ति नहीं है), यह विचार करना है इसे उस बिंदु तक कम करें जिसका आपके द्वारा की जा रही गणना में कोई प्रभाव नहीं है।
इकर

@ आप सही हैं। यद्यपि आप, और न ही प्रश्न पूछने वाले व्यक्ति ने निर्दिष्ट किया है कि वे वास्तव में क्या गणनाएं प्राप्त करने की कोशिश कर रहे हैं और वे सटीक चाहते हैं। बंदूक के नंबर सिद्धांत में कूदने से पहले उसे उस सवाल का जवाब देना होगा। केवल यह कहना lot of mathematical calculationsमददगार नहीं है और न ही दिए गए उत्तर। मामले के अधिकांश हिस्से में (यदि आप मुद्रा के साथ काम नहीं कर रहे हैं) तो फ्लोट को वास्तव में पर्याप्त होना चाहिए।
चाड

@ यह एक उचित बिंदु है, वहाँ निश्चित रूप से ओपी से पर्याप्त डेटा नहीं है यह बताने के लिए कि सटीक स्तर क्या है जो उन्हें चाहिए।
इकर

7

फ्लोटिंग पॉइंट अंकगणित आमतौर पर काफी सटीक होता है (a के लिए 15 दशमलव अंक double) और काफी लचीला होता है। जब आप गणित कर रहे होते हैं तो समस्याएँ काफी हद तक कम हो जाती हैं। यहाँ कुछ उदाहरण हैं:

  • घटाव पर निरस्तीकरण:, 1234567890.12345 - 1234567890.12300परिणाम 0.0045में सटीकता के केवल दो दशमलव अंक हैं। जब भी आप समान परिमाण के दो नंबरों को घटाते हैं तो यह हमला करता है।

  • सटीक निगलने: का 1234567890.12345 + 0.123456789012345मूल्यांकन करता है 1234567890.24691, दूसरे ऑपरेंड के अंतिम दस अंक खो जाते हैं।

  • गुणन: यदि आप दो 15 अंकों की संख्या को गुणा करते हैं, तो परिणाम में 30 अंक होते हैं जिन्हें संग्रहीत करने की आवश्यकता होती है। लेकिन आप उन्हें स्टोर नहीं कर सकते, इसलिए पिछले 15 बिट खो गए हैं। जब यह sqrt()(जैसे sqrt(x*x + y*y):: परिणाम केवल परिशुद्धता के 7.5 अंक होगा के साथ संयुक्त है यह विशेष रूप से irksome है ।

ये मुख्य नुकसान हैं जिनसे आपको अवगत होने की आवश्यकता है। और एक बार जब आप उनके बारे में पता कर लेते हैं, तो आप अपने गणित को इस तरह से तैयार करने की कोशिश कर सकते हैं जो उन्हें टालता है। परीक्षा के लिए, यदि आपको एक लूप में बार-बार मान बढ़ाने की आवश्यकता है, तो ऐसा करने से बचें:

for(double f = f0; f < f1; f += df) {

कुछ पुनरावृत्तियों के बाद, बड़ा fकी सटीकता का हिस्सा निगल जाएगा df। इससे भी बदतर, त्रुटियों को जोड़ देगा, जिससे गर्भनिरोधक स्थिति के लिए अग्रणी एक छोटे dfसे समग्र परिणाम खराब हो सकते हैं। बेहतर यह लिखें:

for(int i = 0; i < (f1 - f0)/df; i++) {
    double f = f0 + i*df;

क्योंकि आप वेतन वृद्धि को एक गुणा में जोड़ रहे हैं, जिसके परिणामस्वरूप f15 दशमलव अंक सटीक होंगे।

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


2

यह सुनिश्चित करने के लिए कि आपको समस्याएँ नहीं हैं: फ़्लोटिंग-पॉइंट अंकगणितीय समस्याओं के बारे में जानें, या किसी ऐसे व्यक्ति को नियुक्त करें, जो कुछ सामान्य ज्ञान का उपयोग करता है या करता है।

पहली समस्या सटीक है। कई भाषाओं में आपके पास "फ्लोट" और "डबल" ("डबल सटीक" के लिए डबल खड़ा होता है), और कई मामलों में "फ्लोट" आपको लगभग 7 अंक सटीक देता है, जबकि डबल आपको देता है 15. सामान्य ज्ञान यह है कि यदि आपके पास एक है ऐसी स्थिति जहां परिशुद्धता की समस्या हो सकती है, 15 अंक 7 अंकों की तुलना में एक बहुत अच्छा है। कई छोटी समस्याग्रस्त स्थितियों में, "डबल" का उपयोग करने का मतलब है कि आप इसके साथ दूर हो जाते हैं, और "फ्लोट" का अर्थ है कि आप नहीं। मान लीजिए कि किसी कंपनी का मार्केट कैप 700 बिलियन डॉलर है। फ्लोट में इसका प्रतिनिधित्व करते हैं, और सबसे कम बिट $ 65536 है। डबल का उपयोग करके इसका प्रतिनिधित्व करते हैं, और सबसे कम बिट लगभग 0.012 सेंट है। इसलिए जब तक आप वास्तव में, वास्तव में जानते हैं कि आप क्या कर रहे हैं, आप डबल का उपयोग करते हैं, फ्लोट का नहीं।

दूसरी समस्या अधिक सिद्धांत की बात है। यदि आप दो अलग-अलग गणना करते हैं जो एक ही परिणाम देना चाहिए, तो वे अक्सर गोलाई त्रुटियों के कारण नहीं होते हैं। दो परिणाम जो समान होने चाहिए, वे "लगभग बराबर" होंगे। यदि दो परिणाम एक साथ करीब हैं, तो वास्तविक मूल्य समान हो सकते हैं। या वे नहीं हो सकते हैं। आपको इसे ध्यान में रखना चाहिए और ऐसे कार्यों को लिखना और उपयोग करना चाहिए जो कहते हैं कि "x निश्चित रूप से y से अधिक है" या "x निश्चित रूप से y से कम है" या "x और y समान हो सकता है"।

यदि आप राउंडिंग का उपयोग करते हैं तो यह समस्या बहुत खराब हो जाती है, उदाहरण के लिए "निकटतम पूर्णांक के लिए राउंड x नीचे"। यदि आप 120 * 0.05 गुणा करते हैं, तो परिणाम 6 होना चाहिए, लेकिन जो आपको मिलता है वह "कुछ संख्या 6 के बहुत करीब है"। यदि आप "निकटतम पूर्णांक के लिए राउंड डाउन" करते हैं, तो "6 के करीब संख्या" "6 से थोड़ा कम" हो सकती है और 5. के लिए गोल हो सकता है और ध्यान दें कि आपके पास कितना सटीक है इससे कोई फर्क नहीं पड़ता। इससे कोई फर्क नहीं पड़ता कि आपका परिणाम 6 के करीब है, क्योंकि यह 6 से कम है।

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


1
फ्लोटिंग पॉइंट गणित के बारे में "सामान्य ज्ञान" कुछ भी नहीं है।
whatsisname

इसके बारे में और जानें।
gnasher729

0

ज्यादातर लोग गलती करते हैं जब वे डबल देखते हैं वे बिगडेसिमल चिल्लाते हैं, जब वास्तव में वे समस्या को कहीं और स्थानांतरित कर देते हैं। डबल साइन बिट देता है: 1 बिट, एक्सपोजर चौड़ाई: 11 बिट्स। महत्वपूर्ण सटीकता: 53 बिट्स (52 स्पष्ट रूप से संग्रहीत)। दोहरे की प्रकृति के कारण, आप जितना बड़ा अंतर करते हैं, आप सापेक्ष सटीकता खो देते हैं। हमारे द्वारा उपयोग की जाने वाली सापेक्ष सटीकता की गणना करने के लिए यहाँ bellow है।

गणना में दोहरे की सापेक्ष सटीकता हम निम्नलिखित फोलुमा 2 ^ ई <= एब्स (एक्स) <2 ^ (ई + 1) का उपयोग करते हैं

एप्सिलॉन = 2 ^ (ई -10)% एक 16-बिट फ्लोट (आधा परिशुद्धता) के लिए

 Accuracy Power | Accuracy -/+| Maximum Power | Max Interger Value
 2^-1           | 0.5         | 2^51          | 2.2518E+15
 2^-5           | 0.03125     | 2^47          | 1.40737E+14
 2^-10          | 0.000976563 | 2^42          | 4.39805E+12
 2^-15          | 3.05176E-05 | 2^37          | 1.37439E+11
 2^-20          | 9.53674E-07 | 2^32          | 4294967296
 2^-25          | 2.98023E-08 | 2^27          | 134217728
 2^-30          | 9.31323E-10 | 2^22          | 4194304
 2^-35          | 2.91038E-11 | 2^17          | 131072
 2^-40          | 9.09495E-13 | 2^12          | 4096
 2^-45          | 2.84217E-14 | 2^7           | 128
 2^-50          | 8.88178E-16 | 2^2           | 4

दूसरे शब्दों में, यदि आप +/- 0.5 (या 2 ^ -1) की सटीकता चाहते हैं, तो अधिकतम आकार जो संख्या 2 हो सकता है, वह है 52 ^। इससे बड़ा और फ्लोटिंग पॉइंट नंबरों के बीच की दूरी 0.5 से अधिक है।

यदि आप +/- 0.0005 (लगभग 2 ^ -11) की सटीकता चाहते हैं, तो अधिकतम आकार जो संख्या हो सकती है वह 2 ^ 42 है। इससे बड़ा और फ्लोटिंग पॉइंट नंबरों के बीच की दूरी 0.0005 से अधिक है।

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

हालांकि यह कुछ कहा जाना है, यदि आप केवल 100 मीटर की दुनिया में 100 मीटर की दूरी पर अनुकरण करने का लक्ष्य रखते हैं, तो आप 2 ^ -45 के पास सटीकता के क्रम में कहीं और जा सकते हैं। यह भी पता नहीं चल रहा है कि सीपीयू के अंदर आधुनिक एफपीयू कैसे मूल प्रकार के आकार के बाहर की गणना करेगा और गणना पूरी होने के बाद ही वे मूल प्रकार के आकार के अनुसार (एफपीयू राउंडिंग मोड के आधार पर) गोल करेंगे।

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