2014-09-06 2 views
5

यह इस सवाल का एक अनुसरण है: Why does a division result differ based on the cast type?कास्ट प्रकार के आधार पर विभाजन का परिणाम अलग क्यों होता है? (फॉलोअप)

त्वरित सारांश:

byte b1 = (byte)(64/0.8f); // b1 is 79 
int b2 = (int)(64/0.8f); // b2 is 79 
float fl = (64/0.8f); // fl is 80 

सवाल यह है: क्यों परिणाम डाली प्रकार के आधार पर अलग हैं? एक जवाब देने के दौरान मैं एक मुद्दे में भाग गया, मैं समझाने में सक्षम नहीं था।

01000010 10100000 00000000 00000000 

आईईईई 754 प्रारूप में विभाजित करना:

var bytes = BitConverter.GetBytes(64/0.8f).Reverse(); // Reverse endianness 
var bits = bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')); 
Console.WriteLine(string.Join(" ", bits)); 

यह निम्न आउटपुट

0 10000101 01000000000000000000000 

साइन इन करें:

0 => Positive 

घातांक:

10000101 => 133 in base 10 

अपूर्णांश:

01000000000000000000000 => 0*2^-1 + 1*2^-2 + 0*2^-3 ... = 1/4 = 0.25 

दशमलव प्रतिनिधित्व:

(1 + 0.25) * 2^(133 - 127) (Subtract single precision bias) 

यह ठीक 80 तो परिणाम में क्यों परिणाम एक फर्क कास्टिंग करता है?

+3

आगे अनुसंधान करने के लिए उपरोक्त - महान खोज! – Matthias

उत्तर

4

अन्य सूत्र में मेरा जवाब पूरी तरह से सही नहीं है:। असल में, जब रनटाइम पर अभिकलन, (byte)(64/0.8f) है।

जब एक float कास्टिंग 64/0.8f का परिणाम युक्त byte रनटाइम पर करने के लिए, परिणाम वास्तव में 80 हालांकि, इस मामले में जब डाली काम के एक भाग के रूप में किया जाता है नहीं है:

float f1 = (64/0.8f); 

byte b1 = (byte) f1; 
byte b2 = (byte)(64/0.8f); 

Console.WriteLine(b1); //80 
Console.WriteLine(b2); //79 

जबकि बी 1 में अपेक्षित परिणाम शामिल है, बी 2 बंद है। disassembly के अनुसार, बी 2 निम्नलिखित के रूप में असाइन किया गया है:

mov   dword ptr [ebp-48h],4Fh 

इस प्रकार, संकलक रनटाइम पर परिणाम से एक अलग परिणाम की गणना करने लगता है। मुझे नहीं पता, हालांकि, अगर यह अपेक्षित व्यवहार है या नहीं।

संपादित: शायद यह प्रभाव पास्कल Cuoq वर्णित है: संकलन समय के दौरान, सी # संकलक double का उपयोग करता है अभिव्यक्ति की गणना करने के। इसका परिणाम 7 9, xxx है जो 79 तक छोटा हो गया है (एक डबल के रूप में एक समस्या का कारण बनने के लिए पर्याप्त परिशुद्धता है)।
फ्लोट का उपयोग करते हुए, हालांकि, हम वास्तव में किसी समस्या में नहीं चलते हैं, क्योंकि फ़्लोटिंग-पॉइंट "त्रुटि" एक फ्लोट की सीमा के भीतर नहीं होती है।

रनटाइम के दौरान, यह एक भी प्रिंट 79:

double d1 = (64/0.8f); 
byte b3 = (byte) d1; 
Console.WriteLine(b3); //79 

EDIT2:

int sixtyfour = Int32.Parse("64"); 
byte b4 = (byte)(sixtyfour/0.8f); 
Console.WriteLine(b4); //79 

परिणाम 79 तो है: पास्कल Cuoq के अनुरोध के रूप में, मैं निम्नलिखित कोड भाग गया उपरोक्त कथन कि संकलक और रनटाइम एक अलग परिणाम की गणना सही नहीं है।

EDIT3:

byte b5 = (byte)(float)(sixtyfour/0.8f); 
Console.WriteLine(b5); //80 

हालांकि, ध्यान दें कि इस मामले में जब में (परिणाम लिख नहीं है: जब (पास्कल Cuoq के लिए क्रेडिट, फिर से) करने के लिए पिछले कोड में परिवर्तन, परिणाम 80 है 79): (byte)(64/0.8f) एक float के रूप में मूल्यांकन नहीं है, लेकिन एक double के रूप में मूल्यांकन (byte करने के लिए इसे कास्टिंग) से पहले:

byte b6 = (byte)(float)(64/0.8f); 
Console.WriteLine(b6); //79 

तो यहाँ क्या हो रहा प्रतीत हो रहा है है। इसका परिणाम एक गोल त्रुटि में होता है (जो तब नहीं होता है जब गणना float का उपयोग करके की जाती है)। दो बार कास्टिंग करने से पहले तैरने के लिए एक स्पष्ट कलाकार (जिसे रीशेपर, बीटीडब्लू द्वारा अनावश्यक के रूप में चिह्नित किया जाता है) इस मुद्दे को हल करता है। हालांकि, जब संकलन समय के दौरान गणना की जाती है (संभवतः केवल स्थिरांक का उपयोग करते समय), float पर स्पष्ट कास्ट अनदेखा/अनुकूलित किया जाता है।

टीएलडीआर: फ़्लोटिंग पॉइंट गणना प्रारंभिक रूप से दिखाई देने से कहीं अधिक जटिल होती है।

+1

मुझे सी # द्वारा उपयोग की जाने वाली सटीक परिभाषा नहीं है, लेकिन अन्य भाषाओं में जो इंटरमीडिएट फ्लोटिंग-पॉइंट कंप्यूटेशंस के लिए अतिरिक्त परिशुद्धता की अनुमति देती है, जैसे सी, किसी भी असाइनमेंट को प्रकार की सटीकता के लिए गोल करना पड़ता है। यदि सी # वही काम करता है, तो 'फ्लोट एफ 1 = (64/0.8 एफ); बाइट बी 1 = (बाइट) एफ 1; 'को विभाजन के गोलाकार परिणामों के साथ' एफ 1' असाइन करना होगा, और उसी परिणाम को 'बाइट बी 2 = (बाइट) (64/0.8 एफ) के रूप में नहीं देना चाहिए; '। जांचने का तरीका यह है कि 64 प्रोग्राम का इनपुट हो, ताकि न तो अनुकूलित किया जा सके। –

+0

ऐसा लगता है कि आप सही हैं: मैंने अभी ऐसा किया - परिणाम 79 है। मैं अब थोड़ा उलझन में हूं, हालांकि, वास्तव में क्या हो रहा है। – Matthias

+0

मुझे लगता है कि अब मुझे अपने आखिरी संपादन - विचारों के साथ मिल गया है? – Matthias

3

सी # भाषा विनिर्देश to compute intermediate floating-point results at a precision greater than that of the type की अनुमति देता है। यह बहुत संभव है कि यहां क्या हो रहा है।

जबकि 64/0.8 उच्च परिशुद्धता के लिए गणना की 80 की तुलना में थोड़ा कम है (क्योंकि 0.8cannot द्विआधारी फ्लोटिंग प्वाइंट में ठीक वैसे ही प्रदर्शित किया है), और जब एक पूर्णांक प्रकार तक छोटा कर दिया है, तो विभाजन का परिणाम float में बदल जाती है 79 में धर्मान्तरित , यह 80.0f के लिए गोल है।

(फ्लोटिंग-पॉइंट से फ्लोटिंग-पॉइंट के रूपांतरण निकटतम-तकनीकी रूप से हैं, वे एफपीयू के गोलाकार मोड के अनुसार किए जाते हैं, लेकिन सी # एफपीयू के गोलाकार मोड को "से लेकर" । निकटतम "डिफ़ॉल्ट फ्लोटिंग प्वाइंट से आए रूपांतरण पूर्णांक प्रकार काटना)

+0

नोप - 79, भी। – Matthias

+1

@ winSharp93 Arg! मैं बस अनुमान लगा रहा था, हालांकि। उत्तर के उस हिस्से को बेहतर तरीके से हटा दें। –

+0

ऐसा लगता है कि आप सही हैं, लेकिन एक अलग तरीके से (मेरा संपादन देखें): "(बाइट) (64/0.8)" स्थिर है, संकलक संकलन समय के दौरान इसका मूल्यांकन करता है। हालांकि, यह अनुकूलन गणना के लिए फ्लोट के बजाय डबल का उपयोग करके किया जाता है। – Matthias

0

हालांकि सी # एक स्पष्ट डाली किसी भी समय जो कुछ double के रूप में निर्दिष्ट किया जाता है एक float को संग्रहीत किया जाता है की आवश्यकता होती है में जावा के IMHO (दुर्भाग्यपूर्ण) नेतृत्व इस प्रकार है, सी # संकलक द्वारा बनाया गया कोड नेट रनटाइम double के रूप में गणना करने के लिए अनुमति देता है और भाषा नियमों के अनुसार, float होने पर, कई संदर्भों में उन double मानों का उपयोग करें जहां अभिव्यक्ति का प्रकार होना चाहिए।

सौभाग्य से, सी # कंपाइलर यह सुनिश्चित करने के लिए कम से कम एक तरीका प्रदान करता है कि चीजें जो निकटतम प्रतिनिधित्व करने योग्य float पर गोल की जाती हैं, वास्तव में हैं: उन्हें स्पष्ट रूप से float पर डालें।

यदि आप अपनी अभिव्यक्ति (byte)(float)(sixtyFour/0.8f) के रूप में लिखते हैं, तो परिणाम को आंशिक भाग को छूने से पहले निकटतम प्रतिनिधित्व करने योग्य float मान पर गोल करने के लिए मजबूर होना चाहिए। यद्यपि float पर कास्ट अनावश्यक दिखाई दे सकता है (अभिव्यक्ति का संकलन-समय प्रकार पहले से ही float है), कास्ट "चीज जो float होनी चाहिए लेकिन वास्तव में double" है जो वास्तव में float है।

ऐतिहासिक रूप से, कुछ भाषाएं निर्दिष्ट करती हैं कि सभी फ़्लोटिंग-पॉइंट ऑपरेशंस double पर किए जाते हैं; float गति गणना करने के लिए अस्तित्व में नहीं था, लेकिन भंडारण आवश्यकताओं को कम करने के लिए। आमतौर पर स्थिरांक को float के रूप में निर्दिष्ट करने की आवश्यकता नहीं थी, क्योंकि 0.800000000000000044 (double मान 0.8) द्वारा विभाजित होने से 0.800000011920 9 2 9 (मूल्य 0.8f) से विभाजित करने से धीमा नहीं था। सी # कुछ हद तक परेशान करने से float1 = float2/0.8; को "सटीकता के नुकसान" की वजह से अनुमति नहीं दी जाएगी, बल्कि इसके बजाय कम सटीक float1 = float2/0.8f; का समर्थन करता है और संभावित त्रुटि वाले double1 = float1/0.8f; पर भी ध्यान नहीं देता है। तथ्य यह है कि ऑपरेशन float मानों के बीच किया जाता है इसका मतलब यह नहीं है कि परिणाम वास्तव में float होगा, हालांकि - इसका मतलब यह है कि संकलक इसे कुछ संदर्भों में float पर चुपचाप गोलाकार करने की अनुमति देगा लेकिन इसे दूसरों में मजबूर नहीं करेगा ।

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

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