डबल बनाम बिगडिमल?


303

मुझे कुछ फ्लोटिंग पॉइंट वैरिएबल की गणना करनी है और मेरे सहकर्मी ने मुझे BigDecimalइसके बजाय उपयोग करने का सुझाव दिया है doubleक्योंकि यह अधिक सटीक होगा। लेकिन मैं जानना चाहता हूं कि यह क्या है और सबसे अधिक कैसे बनाया जाए BigDecimal?


जवाबों:


446

A BigDecimalसंख्याओं का प्रतिनिधित्व करने का एक सटीक तरीका है। A Doubleकी एक निश्चित सटीकता है। विभिन्न परिमाण (कहते हैं कि के युगल के साथ कार्य करना d1=1000.0और d2=0.001) परिणाम सकता है में 0.001किया जा रहा है alltogether गिरा जब संक्षेप के रूप में परिमाण में अंतर इतना बड़ा है। इसके साथ BigDecimalऐसा नहीं होगा।

इसका नुकसान BigDecimalयह है कि यह धीमा है, और एल्गोरिदम को उस तरह से प्रोग्राम करना थोड़ा मुश्किल है (कारण + - *और /अतिभारित नहीं होना)।

यदि आप पैसे के साथ काम कर रहे हैं, या सटीक होना चाहिए, उपयोग करें BigDecimal। अन्यथा Doublesकाफी अच्छे हो जाते हैं।

मैं पढ़ सिफारिश करते हैं जावाडोक के BigDecimalरूप में वे बातें बेहतर की तुलना में मैं यहाँ करने के लिए समझाने कर :)


हां, मैं स्टॉक के लिए मूल्य की गणना कर रहा हूं, इसलिए मेरा मानना ​​है कि इस मामले में BigDecimal उपयोगी है।
ट्रूंग हा

5
@ ट्रॉन्ग हा: जब आप बिगडेसिमल का उपयोग करना चाहते हैं तो कीमतों के साथ काम करना। और अगर आप उन्हें डेटाबेस में संग्रहीत करते हैं तो आप कुछ ऐसा ही चाहते हैं।
extraneon

98
यह कहना कि "बिगडीकल संख्याओं का प्रतिनिधित्व करने का एक सटीक तरीका है" भ्रामक है। 1/3 और 1/7 को बेस 10 नंबर सिस्टम (बिगडेसिमल) या बेस 2 नंबर सिस्टम (फ्लोट या डबल) में व्यक्त नहीं किया जा सकता है। 1/3 को बेस 3, बेस 6, बेस 9, बेस 12 इत्यादि में बिल्कुल व्यक्त किया जा सकता है और 1/7 को बेस 7, बेस 14, बेस 21, आदि में बिल्कुल व्यक्त किया जा सकता है। BigDecimal के फ़ायदे हैं कि यह मनमाना सटीक है और यह है कि मनुष्य गोलाई त्रुटियों के लिए उपयोग किया जाता है आप में आधार से 10 मिल
procrastinate_later

3
इसके बारे में अच्छी बात यह है कि यह धीमा है, मुझे यह समझने में मदद करता है कि नेटफ्लिक्स रिबन लोड बैलेंसर कोड डबल्स के साथ क्यों व्यवहार करता है, और फिर इस तरह की लाइनें हैं:if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
माइकल

@ कस्टमरोन मुझे लगता है कि आपके कहने का अर्थ है "यदि सटीकता एक है, तो उपयोग करें BigDecimal", एक डबल में अधिक "सटीक" (अधिक अंक) होगा।
jspinella

164

मेरी अंग्रेजी अच्छी नहीं है, इसलिए मैं यहां एक सरल उदाहरण लिखूंगा।

    double a = 0.02;
    double b = 0.03;
    double c = b - a;
    System.out.println(c);

    BigDecimal _a = new BigDecimal("0.02");
    BigDecimal _b = new BigDecimal("0.03");
    BigDecimal _c = _b.subtract(_a);
    System.out.println(_c);

कार्यक्रम का उत्पादन:

0.009999999999999998
0.01

कोई अभी भी डबल का उपयोग करना चाहते हैं? ;)


11
@ वेल्डजोन थैट्स सच नहीं है, इस उदाहरण को देखें: बिगडेसिमल दो = नया बिगडिमल ("2"); BigDecimal आठ = new BigDecimal ("8"); Println (two.divide (आठ)); यह 0.25 प्रिंट करता है।
लुडविग डब्ल्यू

4
युगल अग्र: D
वच

फिर भी, यदि आप एक फ्लोट का उपयोग करते हैं, तो आप उस मामले में
बिगडेसिमल की

3
@ एलियूएक्स फ्लोट 0.03-0.02 के साथ काम कर सकता है, लेकिन अन्य मूल्य अभी भी स्पष्ट हैं: बिगडिमल System.out.println(0.003f - 0.002f);सटीक है:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
मार्टिन

50

दोहरे से दो मुख्य अंतर हैं:

  • मनमाने ढंग से सटीक, इसी तरह BigInteger के लिए वे मनमानी परिशुद्धता और आकार की संख्या शामिल कर सकते हैं
  • बेस 2 के बजाय बेस 10, एक बिगडेसिमल n * 10 ^ स्केल है जहां n एक मनमाना बड़ा हस्ताक्षरित पूर्णांक है और स्केल को दशमलव बिंदु को बाएं या दाएं स्थानांतरित करने के लिए अंकों की संख्या के रूप में सोचा जा सकता है।

मौद्रिक गणना के लिए आपको बिगडेसिमल का उपयोग करने का कारण यह नहीं है कि यह किसी भी संख्या का प्रतिनिधित्व कर सकता है, लेकिन यह सभी संख्याओं का प्रतिनिधित्व कर सकता है जो दशमलव धारणा में प्रतिनिधित्व कर सकते हैं और इसमें मौद्रिक दुनिया में लगभग सभी संख्याएं शामिल हैं (आप कभी भी 1/3 $ स्थानांतरित नहीं करते हैं किसी को)।


2
यह उत्तर सही मायने में बिगडेसिमल को दोहरे से अधिक उपयोग करने के कारण और अंतर की व्याख्या करता है। प्रदर्शन की चिंताएँ गौण हैं।
भंवर

यह 100% सच नहीं है। आपने लिखा है कि एक BigDecimal "n * 10 ^ स्केल" है। जावा केवल नकारात्मक संख्याओं के लिए करता है। तो सही होगा: "unscaledValue × 10 ^ -scale"। सकारात्मक संख्याओं के लिए बिगडेसिमल में "मनमाने ढंग से सटीक पूर्णांक का बिना मूल्य का मान और 32-बिट का पूर्णांक स्केल" होता है, जबकि पैमाना दशमलव बिंदु के दाईं ओर अंकों की संख्या है।
एनओडी

25

यदि आप एक भिन्न मान लिखते हैं 1 / 7जैसे दशमलव मान जो आपको मिलता है

1/7 = 0.142857142857142857142857142857142857142857...

के अनंत क्रम के साथ 142857। चूंकि आप केवल अंकों की एक सीमित संख्या लिख ​​सकते हैं, आप अनिवार्य रूप से एक गोलाई (या ट्रंकेशन) त्रुटि का परिचय देंगे।

एक भिन्नात्मक भाग के साथ द्विआधारी संख्याओं की तरह 1/10या 1/100व्यक्त की गई संख्याओं में दशमलव बिंदु के बाद अनंत संख्या में अंक भी होते हैं:

1/10 = binary 0.0001100110011001100110011001100110...

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

दशमलव संख्या (जैसे BigDecimal), दूसरी ओर, प्रत्येक दशमलव अंक को इस प्रकार संचित करें। इसका मतलब यह है कि एक दशमलव प्रकार एक सामान्य अर्थ में बाइनरी फ्लोटिंग पॉइंट या फिक्स्ड पॉइंट प्रकार से अधिक सटीक नहीं है (अर्थात यह 1/7परिशुद्धता के नुकसान के बिना स्टोर नहीं कर सकता है ), लेकिन यह उन संख्याओं के लिए अधिक सटीक है जिनके पास दशमलव अंकों की एक सीमित संख्या है अक्सर पैसे की गणना के लिए मामला है।

जावा का BigDecimalअतिरिक्त लाभ यह है कि इसमें दशमलव बिंदु के दोनों तरफ अंकों की एक मनमानी (लेकिन परिमित) संख्या हो सकती है, जो केवल उपलब्ध स्मृति द्वारा सीमित है।


7

BigDecimal Oracle की मनमानी-सटीक संख्यात्मक लाइब्रेरी है। बिगडेसीमल जावा भाषा का एक हिस्सा है और यह वित्तीय से लेकर वैज्ञानिक (यह वह जगह है, जहां पर विभिन्न प्रकार के अनुप्रयोगों के लिए उपयोगी है)।

कुछ गणनाओं के लिए युगल का उपयोग करने में कुछ भी गलत नहीं है। मान लीजिए, हालाँकि, आप Math.Pi * Math.Pi / 6 की गणना करना चाहते थे, अर्थात्, दो के वास्तविक तर्क के लिए रीमैन ज़ेटा फ़ंक्शन का मूल्य (एक परियोजना जो मैं वर्तमान में काम कर रहा हूं)। फ्लोटिंग-पॉइंट डिवीजन आपको राउंडिंग त्रुटि की एक दर्दनाक समस्या के साथ प्रस्तुत करता है।

दूसरी ओर बिगडेसीमल में मनमानी परिशुद्धता के लिए भावों की गणना के कई विकल्प शामिल हैं। BigDecimal Java वर्ल्ड में नीचे दिए गए ओरेकल डॉक्यूमेंटेशन में बताए गए तरीकों को जोड़ने, गुणा करने और विभाजित करने का तरीका +, *, और / लें।

http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

तुलना विधि लूप में और उसके लिए विशेष रूप से उपयोगी है।

हालांकि, BigDecimal के लिए निर्माणकर्ताओं के अपने उपयोग में, सावधान रहें। स्ट्रिंग कंस्ट्रक्टर कई मामलों में बहुत उपयोगी है। उदाहरण के लिए, कोड

बिगडेसीमल ऑनथर्ड = नया बिगडेसीमल ("0.33333333333");

सटीकता की एक निर्दिष्ट डिग्री के लिए असीम-दोहराव संख्या का प्रतिनिधित्व करने के लिए 1/3 के एक स्ट्रिंग प्रतिनिधित्व का उपयोग करता है। राउंड-ऑफ त्रुटि JVM के अंदर कहीं अधिक गहरी होने की संभावना है कि राउंड-ऑफ त्रुटियां आपके अधिकांश व्यावहारिक गणनाओं को परेशान नहीं करेंगी। हालांकि, व्यक्तिगत अनुभव से, मैंने राउंड-ऑफ रेंगना देखा है, हालांकि। सेटस्ले विधि इन संदर्भों में महत्वपूर्ण है, जैसा कि ओरेकल दस्तावेज से देखा जा सकता है।


BigDecimal Java की मनमानी-सटीक संख्यात्मक लाइब्रेरी का हिस्सा है । 'इन-हाउस' इस संदर्भ में व्यर्थ है, विशेष रूप से यह आईबीएम द्वारा लिखा गया था।
लोर्ने का

@ ईजेपी: मैंने बिगडेसिमल वर्ग में देखा और सीखा कि इसका केवल एक भाग आईबीएम द्वारा लिखा गया है। नीचे टिप्पणी: /* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
realPK

7

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

static void theDoubleProblem1() {
    double d1 = 0.3;
    double d2 = 0.2;
    System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));

    float f1 = 0.3f;
    float f2 = 0.2f;
    System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));

    BigDecimal bd1 = new BigDecimal("0.3");
    BigDecimal bd2 = new BigDecimal("0.2");
    System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}

आउटपुट:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

इसके अलावा हमारे पास है:

static void theDoubleProblem2() {
    double d1 = 10;
    double d2 = 3;
    System.out.println("Double:\t 10 / 3 = " + (d1 / d2));

    float f1 = 10f;
    float f2 = 3f;
    System.out.println("Float:\t 10 / 3 = " + (f1 / f2));

    // Exception! 
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}

हमें आउटपुट देता है:

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion

परंतु:

static void theDoubleProblem2() {
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}

आउटपुट है:

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