2017-05-30 10 views
7
System.out.println(2.00-1.10) 

औरक्यों फ्लोट करने के लिए कास्टिंग जावा में सही परिणाम उत्पन्न करता है?

दोनों उत्पादन एक ही परिणाम 0.8999999999999999, लेकिन

System.out.println((float)(2.00-1.10)) 

आउटपुट 0.9

डिफ़ॉल्ट रूप से जावा डबल में गणना करता है, तो डाउनकास्टिंग परिणाम को सुधारता है क्यों?

और अगर 1.1 डबल में 1.100000000000001 में बदला तो क्यों

System.out.println((double)(1.10)) आउटपुट 1.1 केवल करता है।

संपादित करें: यह क्यों होता है यह जानने के लिए हमें दोनों उत्तरों को समझने की आवश्यकता है। सबसे पहले कैनोलिक प्रतिनिधित्व वास्तव में निम्न स्तर पर अलग है। इसके बाद toString का वापसी मूल्य बदल दिया गया है/तर्क के निकटतम डबल के साथ मिलान/मिलान किया गया है।

+2

पढ़ा [यह] (https://stackoverflow.com/questions/588004/is-floating-point-math-broken) –

+0

"शायद डिफ़ॉल्ट रूप से जावा दोहरी में गणना करता है", '2.00' और' डिफ़ॉल्ट रूप से 1.10' को युगल माना जाता है (जब तक कि आप स्पष्ट रूप से बताते हैं कि उन्हें 'एफ' प्रत्यय जोड़कर फ्लोट किया जाना चाहिए) तो 'डबल + डबल = डबल' का परिणाम जो बताता है कि कास्टिंग '(डबल) (2.00-1.10) ' कुछ भी नहीं बदलता है। – Pshemo

+0

@biziclop, हालांकि कथित डुप्लिकेट इस प्रश्न से संबंधित है; इस मुद्दे के इस संस्करण में भिन्नता है। तथाकथित डुप्लिकेट इस प्रश्न के सबसे मौलिक पहलू को समझाएगा: क्यों डाउनकास्टिंग समस्या को हल करता है? –

उत्तर

4

यह दस्तावेज़ीकरण द्वारा विशेष रूप से अच्छी तरह से समझाया नहीं गया है, लेकिन Double.toString(double) अनिवार्य रूप से उत्पादन के उत्पादन में कुछ गोल करता है। Double.toString एल्गोरिदम का उपयोग पूरे जावा एसई में किया जाता है, उदाहरण सहित PrintStream.println(double)System.out। प्रलेखन इस कहते हैं:

कितने अंक मीटर या एक का आंशिक भाग के लिए मुद्रित किया जाना चाहिए? आंशिक भाग का प्रतिनिधित्व करने के लिए कम से कम एक अंक होना चाहिए, और इसके अलावा बहुत से, लेकिन केवल उतने ही अधिक अंक जितना अधिक अंक double के आसन्न मूल्यों से तर्क मान को विशिष्ट रूप से अलग करने के लिए आवश्यक हैं। ऐसा लगता है कि x सटीक गणितीय मान है जो इस विधि द्वारा एक सीमित nonzero तर्क डी के लिए उत्पादित दशमलव प्रतिनिधित्व द्वारा दर्शाया गया है। फिर डीdoublex के निकट double मान होना चाहिए; या यदि दो double मान समान रूप से एक्स, के करीब हैं तो उनमें से एक और घ significand0 होना चाहिए कम से कम महत्वपूर्ण बिट होना चाहिए।

दूसरे शब्दों में, यह कहता है कि toString का वापसी मूल्य आवश्यक रूप से तर्क का सटीक दशमलव प्रतिनिधित्व नहीं है। एकमात्र गारंटी यह है कि (मोटे तौर पर बोलने) तर्क किसी अन्य double मान की तुलना में वापसी मूल्य के करीब है।

तो जब आप System.out.println(1.10) और 1.10 छपा है की तरह कुछ करते हैं, यह मतलब यह नहीं है में पारित मूल्य वास्तव में आधार 10 मूल्य 1.10 के बराबर है। बल्कि, अनिवार्य रूप से निम्न होता है:

  • पहले, संकलन के दौरान, शाब्दिक 1.10 की जांच की और निकटतम double मूल्य का उत्पादन करने के गोल है। (यह JLS here में कहते हैं इस के लिए नियम double के लिए Double.valueOf(String) में जैसे विस्तृत कर रहे हैं।)
  • दूसरा, जब कार्यक्रम चलाता है, Double.toString कुछ दशमलव मान जो double मूल्य पिछले चरण में उत्पादित करीब है की एक String प्रतिनिधित्व का उत्पादन किसी भी अन्य double मूल्य से अधिक।

यह सिर्फ इतना है कि दूसरे चरण में String करने के लिए रूपांतरण अक्सर एक String जो पहले चरण में शाब्दिक रूप में ही है पैदा करता है। मुझे लगता है कि यह डिजाइन द्वारा है। हालांकि, शाब्दिक उदा। 1.10double मान नहीं बनाता है जो 1.10 के बराबर है।

आप (क्योंकि वे हमेशा एक double में फिट कर सकते हैं, या float) एक double के वास्तविक मूल्य की खोज कर सकते BigDecimal(double) निर्माता का उपयोग कर:,

एक double एक BigDecimal के लिए एक स्रोत के रूप में इस्तेमाल किया जाना चाहिए जब ध्यान दें कि यह कन्स्ट्रक्टर एक सटीक रूपांतरण प्रदान करता है; यह Double.toString(double) विधि का उपयोग करके को में परिवर्तित करने और फिर BigDecimal(String) कन्स्ट्रक्टर का उपयोग करने के समान परिणाम नहीं देता है। परिणाम प्राप्त करने के लिए, staticvalueOf(double) विधि का उपयोग करें।

// 0.899999999999999911182158029987476766109466552734375 
System.out.println(new BigDecimal((double) (2.00 - 1.10))); 
// 0.89999997615814208984375 
System.out.println(new BigDecimal((float) (2.00 - 1.10))); 

आप देख सकते हैं कि न तो परिणाम वास्तव में है 0.9। यह केवल एक संयोग है कि Float.toString उस मामले में 0.9 का उत्पादन होता है और Double.toString नहीं करता है।


एक साइड नोट के रूप में, (double) (2.00 - 1.10) एक अनावश्यक कलाकार है। 2.00 और 1.10 पहले से ही double अक्षर हैं इसलिए अभिव्यक्ति का मूल्यांकन करने का नतीजा पहले से ही double है। इसके अलावा, float को घटाने के लिए, आपको (float) 2.00 - (float) 1.10 जैसे दोनों ऑपरेटरों को डालने की आवश्यकता है या साहित्य जैसे 2.00f - 1.10f का उपयोग करना होगा। (float) (2.00 - 1.10) केवल float पर परिणाम डालता है।

+0

उन अनावश्यक जानवरों को यह जांचना था कि क्या उन्होंने कोई फर्क नहीं पड़ता है। कोई नहीं, मुझे लगता है। –

+0

हां, कोई फर्क नहीं पड़ता। ऐसा करने के लिए आपका तर्क बिल्कुल ठीक है, हालांकि।प्रासंगिक विनिर्देश यह है: * "एक फ्लोटिंग-पॉइंट शाब्दिक प्रकार 'फ्लोट' प्रकार है यदि यह एएससीआईआई अक्षर 'एफ' या' एफ 'से भरा हुआ है, अन्यथा इसका प्रकार' डबल' है और यह वैकल्पिक रूप से ASCII के साथ पर्याप्त हो सकता है अक्षर 'डी' या' डी' * "। (जो [यहां] से है (https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.2), तुरंत दो ग्रे बॉक्स का पालन करें जो वर्णन करते हैं एक फ्लोटिंग पॉइंट शाब्दिक व्याकरण।) – Radiodef

8

0.9 डबल या फ्लोट के रूप में प्रतिनिधित्व योग्य नहीं है। एसओ पर विभिन्न पदों में फ्लोटिंग पॉइंट गणना के आंतरिक विवरणों का उत्तर दिया गया है, जैसे: Is floating point math broken?

अपने विशिष्ट उदाहरण में, आप डबल और नाव है कि इस कोड के साथ 0.9 के सबसे करीब हैं देख सकते हैं:

System.out.println(new BigDecimal(0.9d)); 
System.out.println(new BigDecimal(0.9f)); 

जो विहित डबल आउटपुट और 0.9 का निरूपण फ्लोट:

0,90000000000000002220446049250313080847263336181640625
0.89999997615814208984375

अब जब आप 2.0 - 1.1 गणना, परिणाम है:

System.out.println(new BigDecimal(2.0-1.1)); 

0,899999999999999911182158029987476766109466552734375

आप देख सकते हैं कि यह है नहीं 0.9 के विहित प्रतिनिधित्व इसलिए आप एक अलग परिणाम मिलता है। 0.9f के विहित प्रतिनिधित्व के रूप में

System.out.println(new BigDecimal((float) (2.0-1.1))); 

0,89999997615814208984375

रिटर्न एक ही नंबर:

हालांकि नाव परिशुद्धता के रूप में अच्छा नहीं है और।

+0

बस एक टिप्पणी: 'BigDecimal' में कोई कन्स्ट्रक्टर नहीं है जो' फ्लोट 'तर्क स्वीकार करता है। जावा बस इसे वापस 'डबल' में कास्टिंग कर रहा है ... –

+1

@UsagiMiyamoto हाँ, लेकिन इससे कोई फर्क नहीं पड़ता, जब आप एक फ्लोट को एक डबल पर डालते हैं तो आपको वही नंबर मिलता है। – assylias

+0

@ केनस्टर टाइपो - धन्यवाद। – assylias

1

आप चर के वास्तविक मान को भ्रमित कर रहे हैं कि स्क्रीन पर मूल्य कैसा प्रदर्शित होता है। आपके उदाहरण में, toString विधि का उपयोग मूल्यों को स्ट्रिंग में प्रदर्शित करने से पहले कनवर्ट करने के लिए किया जाता है।यह कितने स्थानों का उपयोग करने के लिए एक डिफ़ॉल्ट का उपयोग करता है (जो double और float के लिए अलग है)। नीचे मैं स्पष्ट रूप से प्रदर्शित करने के लिए कितनी जगहों सेट:

double d1 = 2 - 1.1; 
float f1 = (float) d1; 

System.out.println(String.format("d1 = %.10f f1 = %.10f", d1, f1)); 
System.out.println(String.format("d1 = %.20f f1 = %.20f", d1, f1)); 
System.out.println(String.format("Using toString, d1 = %s f1 = %s", "" + d1, "" + f1)); 

उत्पादन देता है:

d1 = 0.9000000000 f1 = 0.8999999762 
d1 = 0.89999999999999990000 f1 = 0.89999997615814210000 
Using toString, d1 = 0.8999999999999999 f1 = 0.9 

यह दिखाता है कि float मूल्य कम सटीक है कि double से एक है।

+0

मुझे लगता है कि 'toString' राउंड' फ्लोट 'मान लेकिन क्यों नहीं' डबल '? –

+1

यह दोनों को बंद करता है, लेकिन महत्वपूर्ण अंकों की विभिन्न संख्याओं के लिए। चूंकि 'फ्लोट' कम सटीक है, इसलिए यह इसे और अधिक गोल करता है। यह केवल विशेष मानों का दुर्घटना है कि परिणाम इस उदाहरण के लिए अलग दिखते हैं। फ़्लोटिंग पॉइंट मान लगभग हमेशा अनुमानित होते हैं। मान 0.8 99 99 99 99 99 99 999 और 0.9 अलग दिख सकते हैं, लेकिन वे _very_ एक ही मान के करीब हैं। लगभग सभी व्यावहारिक उद्देश्यों के लिए, वे एक ही मूल्य हैं। यदि यह महत्वपूर्ण है कि स्ट्रिंग प्रस्तुति कैसा दिखता है, तो आपको उपयुक्त प्रारूपों के आधार पर एक प्रारूप का उपयोग करना चाहिए। – pcarter

संबंधित मुद्दे

 संबंधित मुद्दे