2014-07-05 9 views
6

के बाद गोल करने के बिना डबल से दशमलव तक एक "उच्च" परिशुद्धता को दो से एक दशमलव तक परिवर्तित करते समय मैं कनवर्ट के साथ सटीकता खो देता हूं। गोल करने के कारण या तो (दशमलव) कास्टिंग।15 अंक

उदाहरण:

double d = -0.99999999999999956d; 
decimal result = Convert.ToDecimal(d); // Result = -1 
decimal result = (Decimal)(d); // Result = -1 

दशमलव मान Convert.ToDecimal (डबल) द्वारा दिया 15 महत्वपूर्ण अंक की एक अधिकतम शामिल हैं। यदि मान पैरामीटर में 15 से अधिक महत्वपूर्ण अंक हैं, तो यह निकटतम के लिए गोल करने का उपयोग करके गोल किया जाता है।

इसलिए मैं आदेश मेरा सटीक रखने के लिए, मैं एक स्ट्रिंग के लिए अपनी डबल कन्वर्ट करने के लिए और फिर फोन Convert.ToDecimal (STRING):

decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d 

इस विधि काम कर रहा है, लेकिन मैं का उपयोग कर से बचने के लिए चाहते हैं 15 अंकों के बाद गोल किए बिना डबल से दशमलव को बदलने के लिए एक स्ट्रिंग चर?

+0

क्या आप पहले से ही 'डी' के लिए सीमा जानते हैं? मैं छद्म कोड में कुछ हल्के समाधान प्रदान कर सकता हूं (मैं सी # में उन्हें लिखने के लिए पर्याप्त परिचित नहीं हूं) यदि आप जानते हैं कि 'डी' एक श्रेणी में है [-2..2] –

+0

डी हमेशा के बीच होगा [- 1,1] –

+0

मुझे लगता है कि यह कहीं और से आने वाला 'डबल' है जिसे आप नहीं बदल सकते हैं? यदि इसे गेट-गो ('दशमलव डी = -0.9 99 99 99 99 99 99 99 56 मीटर;') से दशमलव के रूप में घोषित किया गया है, तो यह सटीकता को बनाए रखता है। –

उत्तर

4

एक संभावित समाधान d को एन डबल्स के सटीक योग के रूप में विघटित करना है, जिसमें से अंतिम छोटा है और इसमें दशमलव के रूप में परिवर्तित होने वाले सभी पिछड़े महत्वपूर्ण अंक शामिल हैं, और पहले (एन -1) बिल्कुल दशमलव में कनवर्ट करें।

स्रोत d -1.0 और 1.0 के बीच डबल के लिए:

decimal t = 0M; 
    bool b = d < 0; 
    if (b) d = -d; 
    if (d >= 0.5) { d -= 0.5; t = 0.5M; } 
    if (d >= 0.25) { d -= 0.25; t += 0.25M; } 
    if (d >= 0.125) { d -= 0.125; t += 0.125M; } 
    if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; } 
    t += Convert.ToDecimal(d); 
    if (b) t = -t; 

Test it on ideone.com.

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

यह स्ट्रिंग के लिए double से रूपांतरण से सस्ता है, और परिणाम में सटीकता के कुछ अतिरिक्त अंक प्रदान करता है (उपरोक्त चार अगर-फिर-एल्स के लिए सटीकता के चार बिट)।

टिप्पणी: अगर सी # अपने आप में एक उच्च परिशुद्धता पर फ्लोटिंग प्वाइंट संगणना ऐसा करने की अनुमति नहीं किया था, एक अच्छा चाल डेकर बंटवारे उपयोग करने के लिए दो मानों d1 और d2 कि प्रत्येक बिल्कुल बदल जाएगा दशमलव में में d विभाजित करने के लिए हो गया होता। हां, डेकर स्प्लिटिंग केवल आईईईई 754 गुणा और अतिरिक्त की सख्त व्याख्या के साथ काम करता है।


एक और विचार frexp की सी # के संस्करण का उपयोग करने significand s प्राप्त करने के लिए और d की e प्रतिपादक, और गणना करने के लिए (Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal> है।

+0

मैंने आपके कोड नमूने का प्रयास किया है लेकिन मुझे -1 मिलता है। क्या आप इसे अपनी तरफ से पुन: पेश कर सकते हैं? –

+0

@StevenMuhr क्षमा करें, मैं सी # या यहां तक ​​कि किसी भी मंच है जहाँ मैं नेट कोड चला सकते हैं की जरूरत नहीं है, लेकिन मैं ideone.com साथ खेलने के लिए कोशिश करते हैं और कुछ है कि काम करता है के साथ वापस आ जाएगा। –

+0

@ स्टेवेनमुहर विधि 1, परिणाम: 0.9 99 99 99 99 99 99 996। आपको 'd' के संकेत के लिए हैंडलिंग जोड़ने की आवश्यकता है। http://ideone.com/MevINX –

1

दो दृष्टिकोण हैं, जिनमें से एक 2^63 से नीचे मूल्यों के लिए काम करेगा, और दूसरा 2^53 से बड़े मानों के लिए काम करेगा।

छोटे मानों को पूर्ण-संख्या और fractional भागों में विभाजित करें। पूरे नंबर भाग को long और फिर Decimal पर डाला जा सकता है [ध्यान दें कि Decimal पर सीधी कास्ट सटीक नहीं हो सकती है!] Fractional भाग को 9007199254740992.0 (2^53) द्वारा ठीक से गुणा किया जा सकता है, long और फिर Decimal में परिवर्तित हो सकता है, और फिर 9007199254740992.0m द्वारा विभाजित। उस विभाजन के परिणाम को पूरे-संख्या भाग में जोड़ना Decimal मान प्राप्त करना चाहिए जो सही होने के कम-से-कम-अंक के भीतर है [यह ठीक से गोल नहीं हो सकता है, लेकिन अभी भी अंतर्निहित रूपांतरणों से कहीं बेहतर होगा!]

बड़े मानों के लिए, (1.0/281474976710656.0) (2^-48) से गुणा करें, उस परिणाम का पूरा-संख्या हिस्सा लें, इसे 281474976710656.0 तक गुणा करें, और इसे मूल परिणाम से घटाएं। पूरे-संख्या परिणामों को विभाजन और घटाव से Decimal (उन्हें ठीक से परिवर्तित करना चाहिए) में कनवर्ट करें, पूर्व को 281474976710656m से गुणा करें, और बाद वाले को जोड़ें।