2008-10-07 14 views
69

ऐसा लगता है कि घटाव किसी प्रकार की समस्या को ट्रिगर कर रहा है और परिणामी मान गलत है।जावा राउंडिंग डबल समस्या को हल करने के लिए कैसे करें

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d; 

78,75 = 787,5 * 10,0/100D

double netToCompany = targetPremium.doubleValue() - tempCommission; 

708,75 = 787,5 - 78,75

double dCommission = request.getPremium().doubleValue() - netToCompany; 

877,8499999999999 = 1586,6 - 708.75

जिसके परिणामस्वरूप उम्मीद मूल्य 877.85 होगा।

सही गणना सुनिश्चित करने के लिए क्या किया जाना चाहिए?

उत्तर

84

फ़्लोटिंग पॉइंट अंकगणित की परिशुद्धता को नियंत्रित करने के लिए, आपको java.math.BigDecimal का उपयोग करना चाहिए। अधिक जानकारी के लिए जॉन जुकोव्स्की द्वारा The need for BigDecimal पढ़ें।

अपने उदाहरण को देखते हुए, अंतिम पंक्ति BigDecimal का उपयोग करके निम्नानुसार होगी।

import java.math.BigDecimal; 

BigDecimal premium = BigDecimal.valueOf(1586.6d); 
BigDecimal netToCompany = BigDecimal.valueOf(708.75d); 
BigDecimal commission = premium.subtract(netToCompany); 
System.out.println(commission + " = " + premium + " - " + netToCompany); 

यह निम्न आउटपुट में परिणाम देता है।

877.85 = 1586.6 - 708.75 
+7

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

+0

यह सच है। मैं BigDecimal के उपयोग को अधिक सटीक रूप से प्रतिबिंबित करने के लिए अपना उत्तर संपादित करूंगा। –

+4

मैं एक नोट जोड़ूंगा कि BigDecimal _division_ को +, -, * से थोड़ा अलग तरीके से इलाज करने की आवश्यकता है, क्योंकि डिफ़ॉल्ट रूप से यह एक अपवाद फेंक देगा यदि यह एक सटीक मान (1/3, उदा।) वापस नहीं कर सकता है। इसी तरह की स्थिति में मैंने उपयोग किया: BigDecimal.valueOf (ए) .डिवाइड (BigDecimal.valueOf (बी), 25, RoundingMode.HALF_UP)। डबलवैल्यू(), जहां 25 सटीक के अधिकतम अंक (डबल परिणाम से अधिक की आवश्यकता से अधिक)। –

3

this question पर प्रतिक्रियाएं देखें। अनिवार्य रूप से आप जो देख रहे हैं वह फ़्लोटिंग पॉइंट अंकगणित का उपयोग करने का एक प्राकृतिक परिणाम है।

आप कुछ मनमाने ढंग से परिशुद्धता (अपने इनपुट के महत्वपूर्ण अंक?) चुन सकते हैं और इसके परिणाम को गोल कर सकते हैं, अगर आपको ऐसा करने में सहज महसूस होता है।

6

जब भी आप युगल के साथ गणना करते हैं, तो ऐसा हो सकता है। यह कोड आपको 877.85:

डबल उत्तर = Math.round (dCommission * 100000)/100000.0;

+1

बेहतर डिवाइड कि सिर्फ 100000 के बजाय 100000.0 द्वारा; Math.round एक लंबा रिटर्न देता है, इसलिए आप अन्यथा पूर्णांक विभाजन का उपयोग करेंगे। –

4

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

+0

यहां एकमात्र पकड़ यह है कि प्रक्रिया में शुरुआती प्रतिशत का एक अंश खोया जा सकता है - यह वित्तीय ऐप्स के लिए बुरा हो सकता है। यदि यह एक समस्या है, तो आप 1/10 सेंट की संख्या या जो भी सटीकता चाहते हैं उसे बचा सकते हैं। –

+0

अगर मैं समय पर वापस जा सकता हूं और अपना पिछला आत्म "चाल" दे सकता हूं, तो यह होगा। पेनी के रूप में कीमत! (या 1e-3 या 1e-6 - जो अभी भी int के रूप में 2+ मिलियन डॉलर के लिए अच्छा है) – Trenton

10

एक और उदाहरण:

double d = 0; 
for (int i = 1; i <= 10; i++) { 
    d += 0.1; 
} 
System.out.println(d); // prints 0.9999999999999999 not 1.0 

BigDecimal उपयोग के बजाय।

संपादित करें:

इसके अलावा, सिर्फ बाहर बात करने के लिए यह एक 'जावा' गोलाई मुद्दा नहीं है। अन्य भाषाओं में समान प्रदर्शन (हालांकि आवश्यक नहीं) व्यवहार प्रदर्शित करता है। जावा इस संबंध में कम से कम लगातार व्यवहार की गारंटी देता है।

53

पिछले उत्तरों के अनुसार, यह फ्लोटिंग पॉइंट अंकगणित करने का एक परिणाम है।

जैसा कि पिछले पोस्टर ने सुझाव दिया था, जब आप संख्यात्मक गणना कर रहे हैं, तो java.math.BigDecimal का उपयोग करें।

हालांकि, BigDecimal का उपयोग करने के लिए एक गोचा है।जब आप डबल मान से BigDecimal में परिवर्तित कर रहे हैं, तो आपके पास एक नया BigDecimal(double) कन्स्ट्रक्टर या BigDecimal.valueOf(double) स्थैतिक फैक्ट्री विधि का उपयोग करने का विकल्प होता है। स्थिर कारखाने विधि का प्रयोग करें।

डबल निर्माता एक BigDecimal को double की पूरी परिशुद्धता धर्मान्तरित, जबकि स्थिर कारखाने प्रभावी रूप से एक String में बदल देता है, तो धर्मान्तरित एक BigDecimal करने के लिए कि।

जब आप उन सूक्ष्म गोलियों में चल रहे हैं तो यह प्रासंगिक हो जाता है। एक संख्या .585 के रूप में प्रदर्शित हो सकती है, लेकिन आंतरिक रूप से इसका मान '0.5849 99 99 99 99 99 996447286321199499070644378662109375' है। यदि आपने BigDecimal कन्स्ट्रक्टर का उपयोग किया है, तो आपको वह संख्या मिल जाएगी जो 0.585 के बराबर नहीं है, जबकि स्थैतिक विधि आपको 0.585 के बराबर मान देगी।

 
double value = 0.585; 
System.out.println(new BigDecimal(value)); 
System.out.println(BigDecimal.valueOf(value)); 
अपने सिस्टम पर

देता

 
0.58499999999999996447286321199499070644378662109375 
0.585 
+3

मैं कई बार इस समस्या में आया हूं और यह वास्तव में काफी परेशान है! – Richard

+1

दिन बचतकर्ता। thanx! –

+3

नोट, इसका कारण यह है कि स्थैतिक विधि एक असीमित MathContext का उपयोग करती है जिसका व्यावहारिक अर्थ है: नया BigDecimal (मान, नया MathContext (0, RoundingMode.HALF_UP)); –

7

मैं इस प्रकार ऊपर के उदाहरण को संशोधित करेगा:

import java.math.BigDecimal; 

BigDecimal premium = new BigDecimal("1586.6"); 
BigDecimal netToCompany = new BigDecimal("708.75"); 
BigDecimal commission = premium.subtract(netToCompany); 
System.out.println(commission + " = " + premium + " - " + netToCompany); 

इस तरह आप स्ट्रिंग का उपयोग के साथ शुरू की कठिनाइयों से बचने। एक अन्य विकल्प:

import java.math.BigDecimal; 

BigDecimal premium = BigDecimal.valueOf(158660, 2); 
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2); 
BigDecimal commission = premium.subtract(netToCompany); 
System.out.println(commission + " = " + premium + " - " + netToCompany); 

मुझे लगता है कि इन विकल्पों का उपयोग कर डबल्स से बेहतर हैं। वेबपैस संख्याओं में तारों के रूप में शुरू होता है।

1

अब तक ऐसा करने के लिए है कि जावा में सबसे सुंदर और सबसे प्रभावी तरीका:

double newNum = Math.floor(num * 100 + 0.5)/100; 
-1

यद्यपि आप सटीक गणना के लिए युगल उपयोग नहीं करना चाहिए निम्नलिखित चाल मुझे मदद की है, तो आप परिणाम वैसे भी गोलाई कर रहे हैं ।

public static int round(Double i) { 
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001)); 
} 

उदाहरण:

Double foo = 0.0; 
    for (int i = 1; i <= 150; i++) { 
     foo += 0.00010; 
    } 
    System.out.println(foo); 
    System.out.println(Math.round(foo * 100.0)/100.0); 
    System.out.println(round(foo*100.0)/100.0); 

कौन सा प्रिंट:

0.014999999999999965 
0.01 
0.02 

और जानकारी: http://en.wikipedia.org/wiki/Double_precision

+0

चूंकि .5 * को * बाइनरी के रूप में बिल्कुल प्रदर्शित किया जा सकता है अंश, एकमात्र मामला जहां इसका असर होगा, जब मूल्य वैध रूप से अलग होता है। –

+0

@ माइकल: मेरा उदाहरण त्रुटिपूर्ण था। हालांकि समस्या बनी हुई है। इसलिए मैंने एक उदाहरण जोड़ा। – Timon

+3

समस्या यह बनी हुई है कि जब आपका मान वास्तव में * 0 जैसा होता है तो आपकी विधि गलत परिणाम लौटाएगी।0149 99 99 999 - यह मूल रूप से इस तरह की समस्याओं का समाधान करने का गलत तरीका है। –

-3

यह काफी आसान है।

आउटपुट के लिए% .2f ऑपरेटर का उपयोग करें। समस्या सुलझ गयी!

उदाहरण के लिए:

int a = 877.8499999999999; 
System.out.printf("Formatted Output is: %.2f", a); 

का एक प्रिंट उत्पादन में ऊपर कोड परिणाम: 877,85

% .2f ऑपरेटर को परिभाषित करता है कि केवल दो दशमलव स्थानों किया जाना चाहिए।

+0

यह मंथिस सन्निकटन और गोल मुद्दों को हल नहीं करता है! आप जो देखते हैं वह वह नहीं है जो आप काम करते हैं! – Benj

+0

यदि आप 1 दशमलव तक गोल करते हैं, तो आपको 877.8 मिल जाएगा। जो शायद आप नहीं चाहते हैं कि आप चाहते थे। – mortensi

3

यह एक मजेदार मुद्दा है।

टिमन्स के जवाब के पीछे विचार यह है कि आप एक ईपीएसलॉन निर्दिष्ट करते हैं जो सबसे छोटी परिशुद्धता का प्रतिनिधित्व करता है जो एक कानूनी डबल हो सकता है। यदि आप अपने आवेदन में जानते हैं कि आपको 0 से नीचे सटीकता की आवश्यकता नहीं होगी।00000001 तो वह जो सुझाव देता है वह सत्य के बहुत करीब एक सटीक परिणाम प्राप्त करने के लिए पर्याप्त है। उन अनुप्रयोगों में उपयोगी जहां वे अपने अधिकतम परिशुद्धता के सामने जानते हैं (उदाहरण के लिए मुद्रा परिशुद्धता के लिए वित्त, आदि)

हालांकि इसे बंद करने की कोशिश करने में मौलिक समस्या यह है कि जब आप इसे पुनर्विक्रय करने के लिए एक कारक से विभाजित होते हैं तो आप वास्तव में पेश करते हैं सटीक समस्याओं के लिए एक और संभावना है। युगल का कोई भी छेड़छाड़ अलग-अलग आवृत्ति के साथ अपर्याप्त समस्याओं को पेश कर सकता है। आप एक बहुत सार्थक अंक पर गोल करने के लिए कोशिश कर रहे हैं विशेष रूप से अगर उदाहरण के लिए (ताकि आपके ऑपरेंड < 0 कर रहे हैं) यदि आप Timons कोड के साथ निम्नलिखित चलाएँ:

System.out.println(round((1515476.0) * 0.00001)/0.00001); 

1499999.9999999998 में परिणाम होगा जहां लक्ष्य यहाँ पूर्णांक बनाना है 500000 की इकाइयों (यानी हम 1500000 चाहते हैं)

वास्तव में पूरी तरह से सुनिश्चित करने का एकमात्र तरीका है कि आप अपर्याप्तता को समाप्त कर चुके हैं ताकि बड़े पैमाने पर एक बड़े पैमाने पर जा सके। जैसे

System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue()); 

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

यह मुझे बताया गया है कि इसे पुन: स्थापित करने के लिए बिगडिसीमल का उपयोग करने का अंतिम चरण कुछ उपयोग मामलों के लिए हमेशा आवश्यक नहीं होता है जब आप यह निर्धारित कर सकते हैं कि कोई इनपुट मान नहीं है कि अंतिम विभाजन एक त्रुटि को पुन: उत्पन्न कर सकता है। वर्तमान में मुझे नहीं पता कि यह कैसे ठीक से निर्धारित किया जाए ताकि अगर कोई जानता है कि मुझे इसके बारे में सुनने में खुशी होगी।

2

अभी तक बेहतर JScience का उपयोग के रूप में BigDecimal काफी सीमित है (उदाहरण के लिए, कोई sqrt समारोह)

double dCommission = 1586.6 - 708.75; 
System.out.println(dCommission); 
> 877.8499999999999 

Real dCommissionR = Real.valueOf(1586.6 - 708.75); 
System.out.println(dCommissionR); 
> 877.850000000000 
+0

lib के लिए धन्यवाद! लेकिन क्या यह साधारण चीज़ के लिए थोड़ी अधिक अधिभारित नहीं है? – Benj

+0

@ बेन्ज आप गुवा के बारे में भी यही कह सकते हैं;) – Tomasz

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