एक डबल में दशमलव स्थानों को हिलाना


97

इसलिए मेरे पास 1234 के बराबर एक डबल सेट है, मैं इसे 12.34 बनाने के लिए एक दशमलव स्थान को स्थानांतरित करना चाहता हूं

तो यह करने के लिए मैं गुणा ।1 से 1234 दो बार, इस तरह थोड़े

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.println(x);

यह परिणाम प्रिंट करेगा, "12.340000000000002"

वहाँ एक रास्ता है, बस दो दशमलव स्थानों के लिए स्वरूपण के बिना, डबल स्टोर करने के लिए 12.34 सही ढंग से?


30
यहां मूल लेख "व्हाट
bsd

43
क्या कोई कारण है जो आपने नहीं किया x /= 100;?
मार्क इंग्राम

जवाबों:


189

यदि आप उपयोग करते हैं doubleया float, आपको राउंडिंग का उपयोग करना चाहिए या कुछ राउंडिंग त्रुटियों को देखने की अपेक्षा करनी चाहिए। यदि आप ऐसा नहीं कर सकते, तो उपयोग करें BigDecimal

आपके पास समस्या यह है कि 0.1 एक सटीक प्रतिनिधित्व नहीं है, और दो बार गणना करके, आप उस त्रुटि को कम कर रहे हैं।

हालांकि, 100 को सही ढंग से दर्शाया जा सकता है, इसलिए प्रयास करें:

double x = 1234;
x /= 100;
System.out.println(x);

जो प्रिंट करता है:

12.34

यह काम करता है क्योंकि Double.toString(d)आपकी ओर से थोड़ी मात्रा में गोलाई करता है, लेकिन यह बहुत अधिक नहीं है। यदि आप सोच रहे हैं कि यह बिना गोलाई के कैसा दिख सकता है:

System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal(x));

प्रिंट:

0.100000000000000005551115123125782702118158340454101562
12.339999999999999857891452847979962825775146484375

संक्षेप में, फ़्लोटिंग फ़्लोटिंग पॉइंट में समझदार उत्तरों के लिए अपरिहार्य है कि आप यह स्पष्ट रूप से कर रहे हैं या नहीं।


ध्यान दें: x / 100और x * 0.01राउंडिंग एरर की बात आते ही बिल्कुल नहीं हैं। ऐसा इसलिए है क्योंकि पहली अभिव्यक्ति के लिए गोल त्रुटि x के मूल्यों पर निर्भर करती है, जबकि 0.01दूसरे में एक निश्चित गोल त्रुटि होती है।

for(int i=0;i<200;i++) {
    double d1 = (double) i / 100;
    double d2 = i * 0.01;
    if (d1 != d2)
        System.out.println(d1 + " != "+d2);
}

प्रिंट

0.35 != 0.35000000000000003
0.41 != 0.41000000000000003
0.47 != 0.47000000000000003
0.57 != 0.5700000000000001
0.69 != 0.6900000000000001
0.7 != 0.7000000000000001
0.82 != 0.8200000000000001
0.83 != 0.8300000000000001
0.94 != 0.9400000000000001
0.95 != 0.9500000000000001
1.13 != 1.1300000000000001
1.14 != 1.1400000000000001
1.15 != 1.1500000000000001
1.38 != 1.3800000000000001
1.39 != 1.3900000000000001
1.4 != 1.4000000000000001
1.63 != 1.6300000000000001
1.64 != 1.6400000000000001
1.65 != 1.6500000000000001
1.66 != 1.6600000000000001
1.88 != 1.8800000000000001
1.89 != 1.8900000000000001
1.9 != 1.9000000000000001
1.91 != 1.9100000000000001

26
मुझे विश्वास नहीं हो रहा है कि मैंने पहली बार में ऐसा करने के बारे में नहीं सोचा था!
साभार

6
यद्यपि 100 को बाइनरी प्रारूप में बिल्कुल प्रतिनिधित्व किया जा सकता है, 100 द्वारा विभाजन को बिल्कुल प्रतिनिधित्व नहीं किया जा सकता है। इस प्रकार, लेखन 1234/100, जैसा कि आपने किया है, वास्तव में अंतर्निहित समस्या के बारे में कुछ भी नहीं करता है - यह लेखन के बराबर होना चाहिए 1234 * 0.01
ब्रूक्स मूसा

1
@Peter लॉरी: क्या आप अधिक बता सकते हैं कि संख्या विषम क्यों है या यहां तक ​​कि गोलाई को भी प्रभावित करेगा? मुझे लगता है कि / = 100 और * = = 01 समान होगा क्योंकि भले ही 100 एक इंट है, लेकिन यह 100.0 किसी भी प्रकार में परिवर्तित किया जाएगा।
eremzeit

1
/100और *0.01एक दूसरे के बराबर हैं, लेकिन ओपी के लिए नहीं *0.1*0.1
आमदन

1
मैं बस इतना कह रहा हूं कि 0.1 से गुणा करने पर औसतन एक बार 0.01 से गुणा करने की तुलना में अधिक से अधिक त्रुटि होगी; लेकिन मैं खुशी से @ JasperBekkers की बात को 100 अलग-अलग होने के बारे में बताऊंगा, बिल्कुल बाइनरी-प्रतिनिधित्व योग्य होने के नाते।
आमदन

52

नहीं - यदि आप दशमलव मानों को सही तरीके से संग्रहित करना चाहते हैं, तो उपयोग करें BigDecimaldoubleबस 0.1 की तरह एक संख्या का प्रतिनिधित्व नहीं कर सकते , किसी भी अधिक आप दशमलव अंकों की एक सीमित संख्या के साथ एक तिहाई के मूल्य लिख सकते हैं।


46

अगर यह सिर्फ स्वरूपण है, तो प्रिंटफ की कोशिश करें

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.printf("%.2f",x);

उत्पादन

12.34

8
उच्च श्रेणीबद्ध उत्तर तकनीकी रूप से अधिक व्यावहारिक हैं, लेकिन यह ओपी की समस्या का सही उत्तर है। हम आम तौर पर दोहरे के मामूली अशुद्धि के बारे में परवाह नहीं करते हैं , इसलिए बिगडेकिमल ओवरकिल है, लेकिन जब हम अक्सर प्रदर्शित करते हैं तो यह सुनिश्चित करना चाहते हैं कि हमारा आउटपुट हमारे अंतर्ज्ञान से मेल खाता है, इसलिए System.out.printf()जाने का सही तरीका है।
डिमो ४१४

28

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

int i=1234;
printf("%d.%02d\r\n",i/100,i%100);

कक्षा में, हमें सामान्य रूप से पूछा गया था कि आधार में कौन सी संख्याओं का सही प्रतिनिधित्व किया जा सकता है।

के लिये base=p1^n1*p2^n2 ... आप किसी भी N का प्रतिनिधित्व कर सकते हैं जहाँ N = n * p1 ^ m1 * P2 ^ m2 है।

चलो base=14=2^1*7^1 ... आप 1/7 1/14 1/28 1/49 का प्रतिनिधित्व कर सकते हैं लेकिन 1/3 का नहीं

मुझे वित्तीय सॉफ़्टवेयर के बारे में पता है - मैंने टिकटमास्टर की वित्तीय रिपोर्ट को वैक्स एएसएम से PASCAL में बदल दिया। पेनीज़ के कोड के साथ उनके पास अपना स्वयं का प्रारूप था। रूपांतरण का कारण 32 बिट पूर्णांक अब पर्याप्त नहीं थे। +/- 2 बिलियन पेनी 20 मिलियन डॉलर है और जो विश्व कप या ओलंपिक के लिए बह निकला, मैं भूल गया।

मुझे गोपनीयता की शपथ दिलाई गई। ओह अच्छा। अकादमिया में, यदि आप इसे प्रकाशित करते हैं तो अच्छा है; उद्योग में, आप इसे गुप्त रखते हैं।


12

आप पूर्णांक संख्या प्रतिनिधित्व का प्रयास कर सकते हैं

int i =1234;
int q = i /100;
int r = i % 100;

System.out.printf("%d.%02d",q, r);

5
@ दान: क्यों? यह वित्तीय एप्स (या किसी अन्य एप्स के लिए सही दृष्टिकोण है जहां एक छोटी सी गोल त्रुटि भी अस्वीकार्य है), जबकि अभी भी हार्डवेयर-स्तर की गति बनाए रखता है। (बेशक, यह एक कक्षा में लपेटा जाएगा, आम तौर पर, हर बार नहीं लिखा जाता है)
अमदन

7
इस समाधान के साथ थोड़ी समस्या है - यदि शेष r10 से कम है, तो कोई 0 पेडिंग नहीं होती है और 1204 12.4 का परिणाम देगा। सही स्वरूपण स्ट्रिंग "% d।% 02d" के समान है
जेकबमैन

10

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


अर्घ, मैं केवल एक ही स्थान से जोड़ने वाला स्पष्टीकरण लिख रहा था। +1।
पोप

@ लॉर्ड हाहा, सॉरी। मैं किसी भी तरह स्कीट हो गया। :-)
कैनपिस

मुझे लगा कि क्यों, लेकिन मुझे आश्चर्य है कि अगर दशमलव स्थान को स्थानांतरित करने के लिए कुछ रचनात्मक तरीका है? क्योंकि एक डबल में 12.34 को साफ करके स्टोर करना संभव है, यह सिर्फ 1 से गुणा करना पसंद नहीं करता है
BlackCow

1
अगर 12.34 को एक डबल में सफाई से स्टोर करना संभव था, तो आपको नहीं लगता कि जावा ने ऐसा किया होगा? यह। आपको कुछ अन्य डेटाटाइप (जैसे BigDecimal) का उपयोग करना होगा। इसके अलावा, आप इसे लूप में करने के बजाय सिर्फ 100 से विभाजित क्यों नहीं करते हैं?

Do'h ... हाँ, एक साफ 12.34 में 100 परिणामों से विभाजित ... धन्यवाद :-P
BlackCow

9

मजेदार है कि कई पोस्ट BigDecimal का उपयोग करने का उल्लेख करते हैं, लेकिन BigDecimal के आधार पर सही उत्तर देने के लिए कोई भी परेशान नहीं करता है? क्योंकि BigDecimal के साथ भी, आप अभी भी गलत हो सकते हैं, जैसा कि इस कोड द्वारा दर्शाया गया है

String numstr = "1234";
System.out.println(new BigDecimal(numstr).movePointLeft(2));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal(0.01)));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal("0.01")));

यह आउटपुट देता है

12.34
12.34000000000000025687785232264559454051777720451354980468750
12.34

बिगडेसिमल कंस्ट्रक्टर ने विशेष रूप से उल्लेख किया है कि स्ट्रिंग कंस्ट्रक्टर का उपयोग संख्यात्मक कंस्ट्रक्टर की तुलना में करना बेहतर है। अंतिम परिशुद्धता भी वैकल्पिक MathContext से प्रभावित होती है।

बिगडेसिमल जावदोक के अनुसार बिगडेसिमल बनाना संभव है जो बिल्कुल 0.1 के बराबर है, बशर्ते आप स्ट्रिंग कंस्ट्रक्टर का उपयोग करें।


5

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

नंबर क्रंचिंग पर कोई भी सभ्य किताब इसका वर्णन करती है। उदाहरण के लिए: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

और आपके प्रश्न का उत्तर देने के लिए:

गुणा के बजाय डिवाइड का उपयोग करें, इस तरह से आपको सही परिणाम मिलता है।

double x = 1234;
for(int i=1;i<=2;i++)
{
  x =  x / 10.0;
}
System.out.println(x);

3

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

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