क्या फ्लोटिंग-पॉइंट गणित C # में सुसंगत है? यह हो सकता है?


155

नहीं, यह एक और "क्यों (1 / 3.0) * 3! = 1" प्रश्न नहीं है।

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

यह वीडियो गेम के लिए एक समस्या है जो रिप्ले स्टोर करता है, या पीयर-टू-पीयर नेटवर्केड (सर्वर-क्लाइंट के विपरीत) है, जो प्रोग्राम को चलाने पर हर बार ठीक उसी परिणाम उत्पन्न करने वाले सभी क्लाइंट पर भरोसा करते हैं - एक समस्या में एक छोटी सी विसंगति फ्लोटिंग-पॉइंट गणना विभिन्न मशीनों (या यहां तक कि एक ही मशीन पर! ) पर काफी भिन्न गेम-स्टेट हो सकती है

यह उन प्रोसेसर के बीच भी होता है जो IEEE-754 को "फॉलो" करते हैं , मुख्यतः क्योंकि कुछ प्रोसेसर (अर्थात् x86) डबल विस्तारित परिशुद्धता का उपयोग करते हैं । यही है, वे सभी गणना करने के लिए 80-बिट रजिस्टरों का उपयोग करते हैं, फिर 64- या 32-बिट्स को काटते हैं, जिससे मशीनों की तुलना में अलग-अलग राउंडिंग परिणाम होते हैं जो गणना के लिए 64- या 32- बिट्स का उपयोग करते हैं।

मैंने इस समस्या के कई समाधान ऑनलाइन देखे हैं, लेकिन C ++ के लिए सभी, C #:

  • डबल विस्तारित-सटीक मोड को अक्षम करें (ताकि सभी doubleगणना IEEE-754 64-बिट्स) _controlfp_s(विंडोज), _FPU_SETCW(लिनक्स?), या fpsetprec(बीएसडी) का उपयोग करें।
  • हमेशा एक ही कंपाइलर सेटिंग्स के साथ एक ही कंपाइलर चलाएं, और सभी उपयोगकर्ताओं को एक ही सीपीयू आर्किटेक्चर (कोई क्रॉस-प्लेटफ़ॉर्म प्ले) की आवश्यकता नहीं है। क्योंकि मेरा "कंपाइलर" वास्तव में जेआईटी है, जो हर बार प्रोग्राम चलने पर अलग-अलग अनुकूलन कर सकता है , मुझे नहीं लगता कि यह संभव है।
  • निश्चित-बिंदु अंकगणित का उपयोग करें, floatऔर doubleपूरी तरह से बचें । decimalइस उद्देश्य के लिए काम करेगा, लेकिन यह बहुत धीमा होगा, और System.Mathपुस्तकालय कार्यों में से कोई भी इसका समर्थन नहीं करता है।

तो, क्या यह C # में भी एक समस्या है? क्या होगा यदि मैं केवल विंडोज (मोनो नहीं) का समर्थन करना चाहता हूं?

यदि यह है, तो क्या मेरे कार्यक्रम को सामान्य डबल-परिशुद्धता पर चलने के लिए मजबूर करने का कोई तरीका है?

यदि नहीं, तो क्या कोई पुस्तकालय है जो फ्लोटिंग-पॉइंट गणनाओं को सुसंगत रखने में मदद करेगा ?


मैंने इस प्रश्न को देखा है , लेकिन हर उत्तर या तो समस्या को बिना किसी समाधान के दोहराता है, या कहता है "इसे अनदेखा करें", जो एक विकल्प नहीं है। मैंने gamedev पर एक समान प्रश्न पूछा , लेकिन (श्रोताओं के कारण) अधिकांश उत्तर C ++ की ओर देखे जा रहे हैं।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

1
उत्तर नहीं है, लेकिन मुझे यकीन है कि अधिकांश डोमेन में आप अपने सिस्टम को इस तरह से डिजाइन कर सकते हैं कि सभी साझा स्थिति नियतात्मक है, और इस वजह से कोई महत्वपूर्ण प्रदर्शन गिरावट नहीं है
ड्रिवुस्किन

1
@Peter .net के लिए आप किसी भी तेज़ फ़्लोटिंग पॉइंट एमुलेशन के बारे में जानते हैं?
कोडइन्चोएज

1
क्या जावा इस समस्या से ग्रस्त है?
जोश

3
@ जोश: जावा में strictfpकीवर्ड है, जो सभी गणनाओं को एक विस्तारित आकार के बजाय निर्दिष्ट आकार ( floatया double) में करने के लिए मजबूर करता है । हालांकि, जावा को अभी भी IEE-754 समर्थन के साथ कई समस्याएं हैं। बहुत (बहुत, बहुत) कुछ प्रोग्रामिंग भाषाएं IEE-754 अच्छी तरह से समर्थन करती हैं।
पोर्स

जवाबों:


52

मुझे पता है कि सामान्य फ्लोटिंग पॉइंट्स को नेट में निर्धारित करने का कोई तरीका नहीं है। JITTER को अलग-अलग प्लेटफ़ॉर्म पर (या .net के विभिन्न संस्करणों के बीच) अलग-अलग व्यवहार करने वाला कोड बनाने की अनुमति है। इसलिए floatनियतात्मक .net कोड में सामान्य एस का उपयोग करना संभव नहीं है।

मैं जिन कार्यदक्षताओं पर विचार करता था:

  1. फिक्स्ड HTTP32 को C # में लागू करें। हालांकि यह बहुत कठिन नहीं है (मेरे पास आधा तैयार कार्यान्वयन है) मूल्यों की बहुत छोटी श्रृंखला इसका उपयोग करने के लिए कष्टप्रद बनाती है। आपको हर समय सावधान रहना होगा ताकि आप न तो अतिप्रवाह करें, न ही बहुत अधिक सटीकता खोएं। अंत में मैंने पाया कि यह सीधे पूर्णांक का उपयोग करने से आसान नहीं है।
  2. फिक्स्ड HTTP64 को C # में लागू करें। मुझे ऐसा करने में मुश्किल हुई। कुछ परिचालनों के लिए 128bit का मध्यवर्ती पूर्णांक उपयोगी होगा। लेकिन .net ऐसे प्रकार की पेशकश नहीं करता है।
  3. एक कस्टम 32 बिट फ़्लिप पॉइंट लागू करें। BitScanReverse आंतरिक की कमी इसे लागू करते समय कुछ झुंझलाहट का कारण बनती है। लेकिन वर्तमान में मुझे लगता है कि यह सबसे आशाजनक मार्ग है।
  4. गणित के संचालन के लिए मूल कोड का उपयोग करें। प्रत्येक गणित ऑपरेशन पर एक प्रतिनिधि कॉल के ओवरहेड को जोड़ता है।

मैंने अभी-अभी 32 बिट फ़्लोटिंग पॉइंट गणित का एक सॉफ्टवेयर कार्यान्वयन शुरू किया है। यह मेरे 2.66GHz i3 पर प्रति सेकंड लगभग 70 मिलियन जोड़ / गुणा कर सकता है। https://github.com/CodesInChaos/SoftFloat । जाहिर है यह अभी भी बहुत अधूरा और छोटी है।


2
वहाँ एक "असीमित" आकार पूर्णांक उपलब्ध BigInteger हालांकि मूल int या लंबे समय के रूप में उपवास के रूप में उपलब्ध नहीं है। इसलिए .NET ऐसा प्रकार प्रदान करता है (F # के लिए बनाया गया है, लेकिन मेरा मानना ​​है कि C # में उपयोग किया जा सकता है)
Rune FS

एक और विकल्प .NET के लिए GNU MP रैपर है । यह जीएनयू मल्टीपल प्रिसिजन लाइब्रेरी के आसपास एक रैपर है जो "इनफिनिट" सटीक पूर्णांक, परिमेय (अंश), और फ्लोटिंग पॉइंट नंबर का समर्थन करता है।
कोल जॉनसन 1

2
यदि आप इनमें से कोई भी करने जा रहे हैं, तो आप decimalपहले से ही कोशिश कर सकते हैं , क्योंकि यह करना बहुत सरल है। केवल अगर यह हाथ में कार्य के लिए बहुत धीमा है, तो अन्य दृष्टिकोणों के बारे में सोचने लायक होगा।
रोमन स्टार्कोव

मैंने एक विशेष मामले के बारे में सीखा है जहां फ्लोटिंग पॉइंट निर्धारक होते हैं। स्पष्टीकरण मुझे मिला है: गुणन / विभाजन के लिए, यदि FP संख्याओं में से एक दो संख्या (2 ^ x) की शक्ति है, तो महत्वपूर्ण / मंटिसा गणना के दौरान नहीं बदलेगी। केवल घातांक बदल जाएगा (बिंदु हट जाएगा)। इसलिए गोलाई कभी नहीं होगी। परिणाम निर्धारक होगा।
zigzag

उदाहरण: 2 ^ 32 जैसी संख्या को दर्शाया जाता है (घातांक: 32, मंटिसा: 1)। यदि हम इसे दूसरे फ्लोट (एक्सप, मैन) के साथ गुणा करते हैं, तो परिणाम (एक्सप +32, मैन * 1) है। विभाजन के लिए, परिणाम है (एक्सपो - 32, आदमी * 1)। मंटिसा को 1 से गुणा करने से मंटिसा नहीं बदलता है, इसलिए इससे कोई फर्क नहीं पड़ता कि इसमें कितने बिट्स हैं।
zigzag

28

C # विनिर्देश (§4.1.6 फ़्लोटिंग पॉइंट प्रकार) विशेष रूप से फ़्लोटिंग पॉइंट कंप्यूटेशन की अनुमति देता है ताकि परिणाम की तुलना में अधिक सटीक का उपयोग किया जा सके। तो, नहीं, मुझे नहीं लगता कि आप उन गणनाओं को सीधे .Net में निर्धारित कर सकते हैं। दूसरों ने विभिन्न कार्यदलों का सुझाव दिया, ताकि आप उन्हें आज़मा सकें।


9
मुझे बस एहसास हुआ कि अगर कोई सीरीज़ इकट्ठी करता है तो सी # विनिर्देश वास्तव में मायने नहीं रखता है। यह केवल तभी मायने रखता है जब कोई स्रोत अनुकूलता चाहता है। क्या वास्तव में मायने रखता है सीएलआर विनिर्देश। लेकिन मुझे पूरा यकीन है कि इसकी गारंटी सी # गारंटी के समान ही कमजोर है।
कोडइंचौस

doubleएक बिट के बाद हर बार कास्टिंग करने के लिए नहीं होगा एक पट्टी अवांछित बिट दूर, लगातार परिणाम उपज?
IllidanS4, मोनिका को

2
@ IllidanS4 मुझे नहीं लगता कि लगातार परिणाम की गारंटी होगी।
23

14

निम्नलिखित पृष्ठ उस मामले में उपयोगी हो सकता है, जहां आपको इस तरह के संचालन की पूर्ण पोर्टेबिलिटी की आवश्यकता होती है। यह IEEE 754 मानक के कार्यान्वयन के परीक्षण के लिए सॉफ्टवेयर पर चर्चा करता है, जिसमें फ्लोटिंग पॉइंट ऑपरेशंस के अनुकरण के लिए सॉफ्टवेयर भी शामिल है। अधिकांश जानकारी संभवतः C या C ++ के लिए विशिष्ट है, हालाँकि।

http://www.math.utah.edu/~beebe/software/ieee/

निश्चित बिंदु पर एक नोट

बाइनरी फिक्स्ड पॉइंट नंबर फ्लोटिंग पॉइंट के विकल्प के रूप में भी अच्छी तरह से काम कर सकते हैं, जैसा कि चार बुनियादी अंकगणितीय कार्यों से स्पष्ट है:

  • जोड़ और घटाव तुच्छ है। वे उसी तरह काम करते हैं जैसे पूर्णांक। बस जोड़ या घटाना!
  • दो निश्चित बिंदु संख्याओं को गुणा करने के लिए, दो संख्याओं को गुणा करें और फिर परिभाषित संख्या को भिन्नात्मक बिट्स में बदल दें।
  • दो निश्चित बिंदु संख्याओं को विभाजित करने के लिए, लाभांश को भिन्न बिट्स की निर्धारित संख्या को छोड़ दें, फिर भाजक द्वारा विभाजित करें।
  • इस पेपर के अध्याय चार में बाइनरी फिक्स्ड पॉइंट नंबरों को लागू करने पर अतिरिक्त मार्गदर्शन है।

बाइनरी फिक्स्ड पॉइंट नंबर्स को किसी भी पूर्णांक डेटा प्रकार जैसे कि इंट, लॉन्ग और बिगइंटर, और गैर-सीएलएस-अनुपालन प्रकार यूंट और अलॉन्ग पर लागू किया जा सकता है।

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

// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
//  with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];

5
आपको इसे एक ओपन-सोर्स कोड प्रोजेक्ट साइट पर अपलोड करना चाहिए, जैसे sourceforge या github। यह खोजने में आसान, योगदान करने में आसान, अपने को फिर से शुरू करने आदि के लिए आसान बनाता है। इसके अलावा, कुछ स्रोत-कोड युक्तियां (अनदेखा करने के लिए स्वतंत्र महसूस करें): स्थिरांक के constबजाय उपयोग करें static, ताकि संकलक उन्हें अनुकूलित कर सकें; स्थिर कार्यों के लिए सदस्य कार्यों को प्राथमिकता दें (इसलिए हम कॉल कर सकते हैं, पूर्व के myDouble.LeadingZeros()बजाय IntDouble.LeadingZeros(myDouble)); एकल-अक्षर चर नामों से बचने की कोशिश करें ( MultiplyAnyLengthउदाहरण के लिए, 9, इसका पालन करना बहुत कठिन है)
ब्लूराजा - डैनी पफ्लुगुएफ्ट

का उपयोग करते हुए सावधान रहें uncheckedऔर गैर सीएलएस अनुरूप तरह प्रकार ulong, uintआदि गति प्रयोजनों के लिए - क्योंकि वे शायद ही कभी इस्तेमाल किया जाता है, JIT उन्हें आक्रामक तरीके से अनुकूलन नहीं है, इसलिए का उपयोग कर उन्हें वास्तव में हो सकता है धीमी की तरह सामान्य प्रकार का उपयोग करने से longऔर int। साथ ही, C # में ऑपरेटर ओवरलोडिंग है , जिससे इस परियोजना को काफी फायदा होगा। अंत में, क्या कोई संबद्ध इकाई-परीक्षण हैं? उन छोटी चीजों के अलावा, अद्भुत नौकरी पीटर, यह हास्यास्पद रूप से प्रभावशाली है!
ब्लूराजा - डैनी पफ्लुगुएफ़ट

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

2
मजेदार बात यह है कि जब मैंने आपके ब्लॉग पर पोस्ट किया तो मैंने यह नहीं देखा कि ब्लॉग आपका था। मैंने बस Google + और इसके C # स्पार्क में यह तय करने का निर्णय लिया था कि यह ब्लॉग प्रविष्टि का सुझाव देता है। इसलिए मैंने सोचा "एक ही समय में इस तरह की बात लिखना शुरू करने के लिए हम दोनों के लिए एक उल्लेखनीय संयोग क्या है"। लेकिन निश्चित रूप से हमारे पास एक ही ट्रिगर था :)
कोडइन्कॉउंस

1
इसे जावा में पोर्ट करने में परेशान क्यों करें? जावा ने पहले से ही नियतकालिक फ्लोटिंग पॉइंट गणित की गारंटी दी है strictfp
एंटीमोनी

9

क्या यह C # की समस्या है?

हाँ। विभिन्न आर्किटेक्चर आपकी चिंताओं के कम से कम हैं, अलग-अलग फ्रैमरेट्स आदि फ्लोट अभ्यावेदन में अशुद्धियों के कारण विचलन पैदा कर सकते हैं - भले ही वे एक ही अशुद्धि हों (जैसे एक ही मशीन पर एक धीमी जीपीयू को छोड़कर)।

क्या मैं System.Decimal का उपयोग कर सकता हूं?

कोई कारण नहीं है कि आप नहीं कर सकते, हालांकि यह कुत्ता धीमा है।

क्या मेरे कार्यक्रम को दोहरे परिशुद्धता में चलाने के लिए बाध्य करने का कोई तरीका है?

हाँ। सीएलआर रनटाइम को स्वयं होस्ट करें ; और CorBindToRuntimeEx को कॉल करने से पहले C ++ एप्लिकेशन में सभी नेसेसरी कॉल / झंडे (जो फ्लोटिंग पॉइंट अंकगणित के व्यवहार को बदलते हैं) को संकलित करें।

क्या कोई लाइब्रेरी है जो फ्लोटिंग पॉइंट गणनाओं को सुसंगत रखने में मदद करेगी?

मेरी जानकारी में नहीं।

क्या इसे हल करने का कोई और तरीका है?

मैंने इस समस्या से पहले ही निपट लिया है, विचार यह है कि QNumbers का उपयोग किया जाए । वे वास्तविक का एक रूप हैं जो निश्चित-बिंदु हैं; लेकिन बेस -10 (दशमलव) में निश्चित बिंदु नहीं - बेस -2 (बाइनरी); इस वजह से उन पर गणितीय प्राइमेटिव्स (जोड़, उप, mul, div) भोले बेस -10 निश्चित बिंदुओं की तुलना में बहुत तेज हैं; खासकर यदि nदोनों मूल्यों के लिए समान है (जो आपके मामले में ऐसा होगा)। इसके अलावा क्योंकि वे अभिन्न हैं वे हर मंच पर अच्छी तरह से परिभाषित परिणाम हैं।

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

क्या मैं QNumbers के साथ अधिक गणितीय कार्यों का उपयोग कर सकता हूं?

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


3
निश्चित नहीं है कि आप फ्रैमरेट्स के मुद्दे के बारे में क्या बात कर रहे हैं। स्पष्ट रूप से आप एक निश्चित अपडेट-दर (उदाहरण के लिए यहां देखें ) चाहते हैं - चाहे वह प्रदर्शन-फ़्रैमरेट अप्रासंगिक हो या न हो। जब तक गलतियाँ सभी मशीनों पर समान हैं, हम अच्छे हैं। मुझे आपका तीसरा जवाब बिल्कुल भी समझ नहीं आया।
ब्लूराजा - डैनी Pflughoeft

@BlueRaja: जवाब "क्या मेरे कार्यक्रम को डबल परिशुद्धता में चलाने के लिए मजबूर करने का एक तरीका है?" संपूर्ण कॉमन लैंग्वेज रनटाइम को फिर से लागू करने के लिए राशि होगी, जो कि बहुत ही जटिल होगी, या C # एप्लीकेशन से C ++ DLL में देशी कॉल का उपयोग करना, जैसा कि यूजर शेलबिटरफ्लाई के जवाब में संकेत दिया गया है। "क्यू न्युट्र्स" को केवल द्विआधारी निश्चित बिंदु संख्याओं के रूप में सोचें, जैसा कि मेरे उत्तर में संकेत दिया गया था (मैंने अब तक द्विआधारी निश्चित बिंदु संख्याओं को "क्यूएनस्यूट्स" नहीं कहा था।)
पीटर ओ।

@ पीटर ओ। आपको रनटाइम को फिर से लागू करने की आवश्यकता नहीं है। जिस सर्वर पर मैं अपनी कंपनी में काम करता हूं, वह CLR रनटाइम को एक देशी C ++ एप्लिकेशन के रूप में होस्ट करता है (इसलिए SQL सर्वर)। मैं आपको Google CorBindToRuntimeEx सुझाव देता हूं।
जोनाथन डिकिंसन

@BlueRaja यह प्रश्न में खेल पर निर्भर करता है। सभी खेलों के लिए निश्चित फ्रैमरेट चरणों को लागू करना एक व्यवहार्य विकल्प नहीं है - क्योंकि एओई एल्गोरिथ्म कृत्रिम विलंबता का परिचय देता है; जो उदाहरण के लिए एक एफपीएस में अस्वीकार्य है।
जोनाथन डिकिंसन

1
@Jonathan: यह सहकर्मी से सहकर्मी खेल है जो केवल इनपुट भेजने में केवल एक मुद्दा है - इन के लिए, आप है एक निश्चित अद्यतन-दर के लिए। अधिकांश एफपीएस इस तरह से काम नहीं करते हैं, लेकिन कुछ जो आवश्यक रूप से एक निश्चित अपडेट-दर है। इस प्रश्न को देखें ।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

6

इस थोड़े पुराने MSDN ब्लॉग प्रविष्टि के अनुसार JIT फ़्लोटिंग पॉइंट के लिए SSE / SSE2 का उपयोग नहीं करेगा, यह सभी x87 है। उसके कारण, जैसा कि आपने उल्लेख किया है कि आपको मोड और झंडे के बारे में चिंता करनी होगी, और सी # में इसे नियंत्रित करना संभव नहीं है। इसलिए सामान्य फ्लोटिंग पॉइंट ऑपरेशंस का उपयोग करना आपके प्रोग्राम के लिए हर मशीन पर सटीक समान परिणाम की गारंटी नहीं देगा।

दोहरी सटीकता के सटीक प्रतिलिपि प्रस्तुत करने योग्यता प्राप्त करने के लिए आपको सॉफ्टवेयर फ़्लोटिंग पॉइंट (या फिक्स्ड पॉइंट) एमुलेशन करना होगा। मुझे ऐसा करने के लिए C # पुस्तकालयों की जानकारी नहीं है।

आपके द्वारा आवश्यक संचालन के आधार पर, आप एकल परिशुद्धता के साथ दूर होने में सक्षम हो सकते हैं। यहाँ विचार है:

  • एकल परिशुद्धता में उन सभी मूल्यों को संग्रहीत करें जिनकी आप परवाह करते हैं
  • एक ऑपरेशन करने के लिए:
    • डबल परिशुद्धता के लिए आदानों का विस्तार
    • डबल परिशुद्धता में ऑपरेशन करते हैं
    • परिणाम को एकल परिशुद्धता में बदलें

X87 के साथ बड़ा मुद्दा यह है कि गणना 53-बिट या 64-बिट सटीकता के साथ की जा सकती है जो कि सटीक ध्वज पर निर्भर करता है और क्या रजिस्टर मेमोरी में भेजा जाता है। लेकिन कई परिचालनों के लिए, उच्च परिशुद्धता में ऑपरेशन करना और निचली सटीकता पर वापस जाना सही उत्तर की गारंटी देगा, जिसका अर्थ है कि उत्तर सभी प्रणालियों पर समान होने की गारंटी होगी। चाहे आप अतिरिक्त सटीकता प्राप्त करें या न करें, क्योंकि आपके पास किसी भी मामले में सही उत्तर की गारंटी देने के लिए पर्याप्त सटीकता है।

संचालन जो इस योजना में काम करना चाहिए: इसके अलावा, घटाव, गुणा, भाग, sqrt। पाप, ऍक्स्प, आदि जैसी चीजें काम नहीं करेंगी (परिणाम आमतौर पर मेल खाएंगे लेकिन कोई गारंटी नहीं है)। "डबल राउंडिंग मासूम कब है?" ACM संदर्भ (भुगतान किया गया रेज।)

उम्मीद है की यह मदद करेगा!


2
यह भी एक समस्या है कि .NET 5, या 6, या 42, अब x87 गणना मोड का उपयोग नहीं कर सकता है। मानक में ऐसा कुछ नहीं है जिसके लिए इसकी आवश्यकता हो।
एरिक जे।

5

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

समाधान के रूप में: आप कम कर सकते हैं (और कुछ प्रयास / प्रदर्शन हिट के साथ) समस्या से पूरी तरह से बचते हैं यदि आप BigIntegerऐसी संख्याओं के किसी भी गणना / भंडारण के लिए एक आम भाजक का उपयोग करके एक निर्धारित परिशुद्धता के लिए अंतर्निहित कक्षा का उपयोग करते हैं और सभी गणनाओं को स्केल करते हैं।

ओपी द्वारा अनुरोध के अनुसार - प्रदर्शन के बारे में:

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

BigInteger"समाधान" कुछ ऐसा ही है - आप को परिभाषित कर सकते केवल यह है कि आप कितना अंक की जरूरत है / चाहते हैं ... शायद आप केवल 80 बिट या परिशुद्धता के 240 बिट्स चाहते हैं।

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

प्रदर्शन को कम करने के लिए कई रणनीतियां हैं - जैसे क्यूएनयूएस (जोनाथन डिकिंसन का उत्तर देखें - क्या फ्लोटिंग-पॉइंट गणित सी # में संगत है? क्या यह हो सकता है? ) और / या कैशिंग (उदाहरण के लिए गणना की गणना ...) आदि।


1
ध्यान दें कि BigIntegerकेवल .net 4.0 में उपलब्ध है।
svick

मेरा अनुमान है कि प्रदर्शन BigIntegerका प्रदर्शन दशमलव से भी अधिक हिट हो गया।
कोडइन्चौसोस

जवाबों में एक-दो बार यहाँ (# जोनाथन Decimalडिकिंसन - 'डॉग स्लो') या BigInteger(@CodeInChaos ऊपर टिप्पणी ) के प्रदर्शन हिट का संदर्भ है - क्या कोई इन प्रदर्शन हिट्स पर थोड़ा स्पष्टीकरण प्रदान कर सकता है और क्या / क्यों वे समाधान प्रदान करने के लिए वास्तव में शो-स्टॉपर्स हैं।
बैरी केय

@ यहिया - संपादन के लिए धन्यवाद - दिलचस्प पठन, हालाँकि, क्या आप कृपया केवल बॉल-पार्क को भी बता सकते हैं कि प्रदर्शन 'फ्लोट' का प्रदर्शन हिट नहीं है, हम 10% धीमी या 10 गुना धीमी गति से बात कर रहे हैं - बस निहित परिमाण के आदेश के लिए एक महसूस करना चाहते हैं।
बैरी केय

यह "केवल 10%" की तुलना में 1: 5 के क्षेत्र में अधिक है
याहिया

2

यहाँ, यह कैसे करना है पर मेरा पहला प्रयास होगा :

  1. एक ATL.dll प्रोजेक्ट बनाएं, जिसमें एक साधारण ऑब्जेक्ट है जो आपके महत्वपूर्ण फ़्लोटिंग पॉइंट ऑपरेशन के लिए उपयोग किया जा सकता है। फ्लोटिंग प्वाइंट करने के लिए किसी भी गैर xx87 हार्डवेयर का उपयोग करने वाले झंडे के साथ इसे संकलित करना सुनिश्चित करें।
  2. फ़ंक्शंस बनाएं जो फ़्लोटिंग पॉइंट ऑपरेशंस को कॉल करें और परिणाम लौटाएं; सरल शुरू करें और फिर अगर यह आपके लिए काम कर रहा है, तो आप बाद में यदि आवश्यक हो तो अपने प्रदर्शन की जरूरतों को पूरा करने के लिए हमेशा जटिलता बढ़ा सकते हैं।
  3. वास्तविक मशीन के चारों ओर control_fp कॉल को यह सुनिश्चित करने के लिए रखें कि यह सभी मशीनों पर उसी तरह से किया गया है।
  4. अपनी नई लाइब्रेरी का संदर्भ लें और यह सुनिश्चित करने के लिए परीक्षण करें कि यह अपेक्षा के अनुरूप काम करती है।

(मेरा मानना ​​है कि आप केवल 32-बिट .dll पर संकलित कर सकते हैं और फिर इसे x86 या AnyCpu के साथ उपयोग कर सकते हैं [या केवल 64-बिट सिस्टम पर x86 को लक्षित करने की संभावना है; नीचे टिप्पणी देखें])

फिर, यह मानकर काम करता है, क्या आप मोनो का उपयोग करना चाहते हैं I मुझे लगता है कि आपको अन्य समान रूप से अन्य x86 प्लेटफार्मों पर पुस्तकालय को दोहराने में सक्षम होना चाहिए (COM के बिल्कुल नहीं; हालांकि, शायद, शराब के साथ? एक बार मेरे क्षेत्र से थोड़ा बाहर? हम हालांकि वहाँ जाते हैं ...)।

मान लें कि आप इसे काम कर सकते हैं, तो आपको कस्टम फ़ंक्शन सेट करने में सक्षम होना चाहिए जो किसी भी प्रदर्शन समस्याओं को ठीक करने के लिए एक साथ कई ऑपरेशन कर सकते हैं, और आपके पास फ़्लोटिंग पॉइंट गणित होगा जो आपको न्यूनतम राशि के साथ प्लेटफार्मों में लगातार परिणाम देने की अनुमति देता है। C ++ में लिखा गया कोड, और अपना शेष कोड C # में छोड़ना।


"32-बिट के लिए संकलित करें। dll और फिर उपयोग करें ... AnyCpu" मुझे लगता है कि यह केवल 32 बिट सिस्टम पर चलने पर काम करेगा। 64 बिट सिस्टम पर केवल एक प्रोग्राम टारगेटिंग x8632 बिट डीएल को लोड करने में सक्षम होगा।
कोडइन्चोस 19

2

मैं गेम डेवलपर नहीं हूं, हालांकि मुझे कम्प्यूटेशनल रूप से कठिन समस्याओं का बहुत अनुभव है ... इसलिए, मैं अपना सर्वश्रेष्ठ प्रदर्शन करूंगा।

मेरे द्वारा अपनाई जाने वाली रणनीति अनिवार्य रूप से यह है:

  • धीमे का उपयोग करें (यदि आवश्यक हो; यदि कोई तेज़ तरीका है, तो बढ़िया!), लेकिन प्रतिलिपि प्रस्तुत करने योग्य परिणाम प्राप्त करने के लिए पूर्वानुमान योग्य तरीका
  • बाकी सब के लिए डबल का उपयोग करें (जैसे, प्रतिपादन)

इस का छोटा समय यह है: आपको एक संतुलन खोजने की आवश्यकता है। यदि आप 30ms रेंडरिंग (~ 33fps) खर्च कर रहे हैं और केवल 1ms टकराव का पता लगा रहे हैं (या कुछ अन्य अति संवेदनशील ऑपरेशन डालें) - भले ही आप महत्वपूर्ण अंकगणित को करने में लगने वाले समय को तिगुना कर लें, इसका असर आपके फ्रेमरेट पर पड़ता है। आप 33.3fps से 30.3fps तक गिरते हैं।

मैं आपको सुझाव देता हूं कि आप सब कुछ प्रोफाइल करते हैं, प्रत्येक महंगे गणना के लिए कितना समय खर्च करते हैं, इस समस्या को हल करने के 1 या अधिक तरीकों के साथ माप दोहराएं और देखें कि प्रभाव क्या है।


1

अन्य उत्तरों में लिंक की जाँच करने से यह स्पष्ट हो जाता है कि आपके पास कभी भी इस बात की गारंटी नहीं होगी कि फ्लोटिंग पॉइंट "सही ढंग से" लागू किया गया है या नहीं, आपको हमेशा किसी दिए गए गणना के लिए एक निश्चित सटीकता प्राप्त होगी, लेकिन शायद आप इसके द्वारा सबसे अच्छा प्रयास कर सकते हैं (1) सभी गणनाओं को एक सामान्य न्यूनतम तक रौंदना (उदाहरण के लिए, यदि अलग-अलग कार्यान्वयन आपको 32 से 80 बिट्स की सटीकता प्रदान करेंगे, तो हमेशा हर ऑपरेशन को 30 या 31 बिट्स तक छोटा कर देंगे), (2) स्टार्टअप पर कुछ परीक्षण मामलों की एक तालिका है (जोड़, घटाना, गुणा, भाग, वर्ग, कोसाइन, आदि के बॉर्डरलाइन मामले) और यदि कार्यान्वयन तालिका से मेल खाने वाले मानों की गणना करता है, तो कोई समायोजन करने से परेशान न हों।


हमेशा हर ऑपरेशन को 30 या 31 बिट्स तक रौंदते हुए - यह वही है जो floatx86 मशीनों पर डेटाटाइप करता है - हालांकि यह उन मशीनों से थोड़ा अलग परिणाम देगा जो केवल 32-बिट्स का उपयोग करके अपनी सभी गणना करते हैं, और ये छोटे परिवर्तन समय के साथ प्रचारित करेंगे। इसलिए, सवाल।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

यदि "एन बिट्स ऑफ प्रिसिजन" का अर्थ है कि कोई भी गणना कई बिट्स के लिए सटीक है, और मशीन ए 32 बिट्स के लिए सटीक है जबकि मशीन बी 48 बिट्स के लिए सटीक है, तो दोनों मशीनों द्वारा किसी भी कैल्क के पहले 32 बिट्स समान होना चाहिए। हर ऑपरेशन के बाद दोनों मशीनों को ठीक से सिंक में रखने के बाद 32 बिट्स या उससे कम के लिए छोटा नहीं होगा? यदि नहीं, तो एक उदाहरण क्या है?
साक्षी प्रोटेक्शन आईडी 44583292

-3

काफी कठिन और तकनीकी सामान O_o में आपका प्रश्न। हालाँकि मुझे एक विचार हो सकता है।

आपको यकीन है कि सीपीयू किसी भी फ्लोटिंग ऑपरेशन के बाद कुछ समायोजन करता है। और सीपीयू कई अलग-अलग निर्देश प्रदान करता है जो अलग-अलग राउंडिंग ऑपरेशन करते हैं।

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

एक गोलाई समायोजन द्वारा की गई 'गलतियाँ' प्रत्येक अगले निर्देश पर बढ़ेंगी।

एक उदाहरण के रूप में हम कह सकते हैं कि एक विधानसभा स्तर पर: एक * b * c एक * c * b के बराबर नहीं है।

मुझे इस पर पूरा यकीन नहीं है, आपको किसी ऐसे व्यक्ति से पूछने की जरूरत होगी जो सीपीयू आर्किटेक्चर को मुझसे ज्यादा जानता हो: पी

हालाँकि आपके प्रश्न का उत्तर देने के लिए: C या C ++ में आप अपनी समस्या को हल कर सकते हैं क्योंकि आपके कंपाइलर द्वारा उत्पन्न मशीन कोड पर आपका कुछ नियंत्रण है, हालाँकि .NET में आपके पास कोई भी नहीं है। इसलिए जब तक आपका मशीन कोड अलग हो सकता है, तब तक आप सटीक परिणाम के बारे में निश्चित नहीं होंगे।

मैं इस तरह से उत्सुक हूं कि यह एक समस्या हो सकती है क्योंकि भिन्नता बहुत कम लगती है, लेकिन अगर आपको वास्तव में सटीक संचालन की आवश्यकता है तो एकमात्र उपाय जिसके बारे में मैं सोच सकता हूं, वह है आपके फ्लोटिंग रजिस्टरों का आकार बढ़ाना। यदि आप यह सुनिश्चित कर सकते हैं कि सीएलआई का उपयोग कर सकते हैं (तो सुनिश्चित करें कि डबल सटीक या लंबी डबल का उपयोग करें)।

मुझे आशा है कि मैं पर्याप्त स्पष्ट हो गया हूं, मैं अंग्रेजी में बिल्कुल सही नहीं हूं (... बिल्कुल भी: s)


9
एक पी 2 पी शूटर की कल्पना करो। आप एक आदमी पर गोली मारते हैं, आप उसे मारते हैं और वह मर जाता है, लेकिन यह बहुत करीब है, आप लगभग चूक गए। दूसरे लड़के के पीसी में थोड़ी अलग गणना का उपयोग किया जाता है और यह गणना करता है कि आप चूक गए हैं। क्या अब आपको समस्या दिख रही है? इस मामले में, रजिस्टरों का आकार बढ़ाने से मदद नहीं मिलेगी (कम से कम पूरी तरह से नहीं)। प्रत्येक कंप्यूटर पर ठीक उसी गणना का उपयोग करना।
svick

5
इस परिदृश्य में आमतौर पर कोई परवाह नहीं करता है कि परिणाम वास्तविक परिणाम के कितने करीब है (जब तक यह उचित है), लेकिन क्या मायने रखता है कि यह सभी उपयोगकर्ताओं के लिए बिल्कुल समान है।
कोडइन्चौसोस

1
आपने सही कहा, हालांकि मैं इस तरह के परिदृश्य के बारे में नहीं था। हालाँकि मैं इस पर @CodeInChaos से सहमत हूँ। मुझे लगता है कि वास्तव में एक महत्वपूर्ण निर्णय लेने के लिए दो बार स्मार्ट नहीं मिला। यह एक सॉफ्टवेयर आर्किटेक्चर मुद्दा अधिक है। एक कार्यक्रम, छूट के लिए शूटर का आवेदन, गणना करना चाहिए और दूसरों को परिणाम भेजना चाहिए। इस तरह से आपको कभी भी त्रुटियाँ नहीं होंगी। आपके पास हिट है या नहीं, लेकिन केवल एक ही डिस्चार्ज लेता है। जैसे @driushkin
AxFab

2
@ असगर: हां, यही कारण है कि ज्यादातर निशानेबाज काम करते हैं; उस "प्राधिकरण" को सर्वर कहा जाता है, और हम समग्र आर्किटेक्चर को "क्लाइंट / सर्वर" आर्किटेक्चर कहते हैं। हालांकि, एक अन्य प्रकार की वास्तुकला है: सहकर्मी से सहकर्मी। पी 2 पी में, कोई सर्वर नहीं है; बल्कि, सभी ग्राहकों को कुछ भी होने से पहले एक दूसरे के साथ सभी कार्यों को सत्यापित करना चाहिए। यह अंतराल को बढ़ाता है, जिससे यह निशानेबाजों के लिए स्वीकार्य नहीं होता है, लेकिन नेटवर्क ट्रैफ़िक को काफी हद तक कम कर देता है, यह उन खेलों के लिए एकदम सही है, जहां एक छोटा अंतराल (~ 250 सेमी) स्वीकार्य है, लेकिन पूरे खेल राज्य को सिंक्रनाइज़ नहीं करना है। अर्थात्, C & C और Starcraft जैसे RTS गेम्स P2P का उपयोग करते हैं।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

5
एक p2p खेल में आप पर भरोसा करने के लिए एक विश्वसनीय मशीन नहीं है। यदि आप एक स्टेशन को यह तय करने की अनुमति देते हैं कि उसकी गोली चली या नहीं, तो आप ग्राहक को धोखा देने की संभावना को नहीं खोलेंगे। इसके अलावा, लिंक भी डेटा की मात्रा को संभाल नहीं सकते हैं जो कभी-कभी परिणाम देते हैं - खेल परिणामों के बजाय आदेश भेजकर काम करते हैं। मैं आरटीएस गेम खेलता हूं और कई बार मैंने इतना कबाड़ उड़ते हुए देखा है कि कोई भी तरीका सामान्य घरेलू अपलिंक पर नहीं भेजा जा सकता है।
लोरेन Pechtel
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.