সি # ফ্লোট এক্সপ্রেশন: ফলাফলটি ভাসা দেওয়ার সময় অদ্ভুত আচরণ


128

আমার নিম্নোক্ত সাধারণ কোড রয়েছে:

int speed1 = (int)(6.2f * 10);
float tmp = 6.2f * 10;
int speed2 = (int)tmp;

speed1এবং speed2একই মান হওয়া উচিত, কিন্তু আসলে, আমার আছে:

speed1 = 61
speed2 = 62

আমি জানি আমার সম্ভবত কাস্টিংয়ের পরিবর্তে ম্যাথ ব্যবহার করা উচিত ound তবে আমি কেন মানগুলি আলাদা তা বুঝতে চাই।

আমি জেনারেটেড বাইটকোডের দিকে তাকালাম, তবে একটি স্টোর এবং বোঝা বাদে অপকডগুলি একই।

আমি জাভাতেও একই কোডটি চেষ্টা করেছি এবং আমি সঠিকভাবে 62 এবং 62 পেয়েছি।

কেউ এই ব্যাখ্যা করতে পারেন?

সম্পাদনা করুন: আসল কোডে এটি সরাসরি 6.2f * 10 নয়, একটি ফাংশন কল * একটি ধ্রুবক। আমার কাছে নিম্নলিখিত বাইকোড রয়েছে:

জন্য speed1:

IL_01b3:  ldloc.s    V_8
IL_01b5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ba:  ldc.r4     10.
IL_01bf:  mul
IL_01c0:  conv.i4
IL_01c1:  stloc.s    V_9

জন্য speed2:

IL_01c3:  ldloc.s    V_8
IL_01c5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ca:  ldc.r4     10.
IL_01cf:  mul
IL_01d0:  stloc.s    V_10
IL_01d2:  ldloc.s    V_10
IL_01d4:  conv.i4
IL_01d5:  stloc.s    V_11

আমরা দেখতে পাচ্ছি যে অপারেশনগুলি ভাসমান এবং একমাত্র পার্থক্য stloc/ldloc

ভার্চুয়াল মেশিন হিসাবে, আমি একই ফলাফলের সাথে মনো / উইন 7, মনো / ম্যাকোস এবং। নেট / উইন্ডোজ দিয়ে চেষ্টা করেছি।


9
আমার অনুমান যে অপারেশনগুলির মধ্যে একটি একক-নির্ভুলতায় হয়েছে এবং অন্যটি ডাবল-স্পষ্টিকনে হয়েছে। এর মধ্যে একটি 62 এর চেয়ে সামান্য কম মানগুলি ফিরিয়েছিল, সুতরাং পূর্ণসংখ্যার সাথে কাটছাঁটি করার সময় 61 ফলন দেয়।
গাবে

2
এগুলি সাধারণত ফ্লোট পয়েন্টের যথার্থ সমস্যা।
টিজেহিউভেল

3
নেট / উইনএক্সপি, নেট / উইন 7, মনো / উবুন্টু এবং মনো / ওএসএক্স এ চেষ্টা করে উভয় উইন্ডোজ সংস্করণের জন্য আপনার ফলাফল দেয়, তবে স্পিড 1 এবং স্পিড 2 উভয় মনো সংস্করণে। ধন্যবাদ @ বোল্টক্লক
ইউজেন

6
মিঃ লিপার্ট ... আপনি চারপাশে ??
ভিসি 74

6
সংকলকের ধ্রুবক অভিব্যক্তি মূল্যায়নকারী এখানে কোনও পুরস্কার জিতছে না। স্পষ্টতই এটি প্রথম প্রকাশে 6.2f কেটে যাচ্ছে, বেস 2 তে এর সঠিক প্রতিনিধিত্ব নেই তাই 6.199999 হিসাবে শেষ হয়। তবে ২ য় অভিব্যক্তিটি তেমন করে না, সম্ভবত এটি কোনওভাবে ডাবল স্পষ্টতায় রাখার ব্যবস্থা করে। এটি অন্যথায় কোর্সের সমান, ভাসমান পয়েন্টের ধারাবাহিকতা কখনই কোনও সমস্যা নয়। এটি স্থির হতে চলেছে না, আপনি কার্যনির্বাহী জানেন।
হ্যানস প্যাস্যান্ট

উত্তর:


168

প্রথমত, আমি ধরে নিলাম যে আপনি জানেন যে 6.2f * 10ভাসমান পয়েন্ট রাউন্ডিংয়ের কারণে ঠিক 62 নয় (এটি আসলে মানটি 61.99999809265137 হিসাবে প্রকাশিত হয় যখনdouble ) এবং আপনার প্রশ্নটি কেবলমাত্র দুটি আপাতদৃষ্টিতে অভিন্ন গণনাগুলি কেন ভুল মানের ফল দেয় তা ।

উত্তরটি হ'ল ক্ষেত্রে (int)(6.2f * 10), আপনি গ্রহণ করছেনdouble 61.99999809265137 মান নিচ্ছেন এবং এটি একটি পূর্ণসংখ্যার সাথে কাটাচ্ছেন, যা 61 ফলন দেয়।

এর ক্ষেত্রে float f = 6.2f * 10আপনি 61.99999809265137 এর দ্বিগুণ মূল্য নিচ্ছেন এবং নিকটতমের কাছে গোল করছেন float, যা 62। আপনি তারপরে এটি কেটে ফেলুনfloat একটি পূর্ণসংখ্যার সাথে ফলাফল 62 62

অনুশীলন: নিম্নলিখিত ক্রিয়াকলাপগুলির ফলাফল ব্যাখ্যা করুন।

double d = 6.2f * 10;
int tmp2 = (int)d;
// evaluate tmp2

আপডেট: মন্তব্যে উল্লিখিত হিসাবে, 6.2f * 10বাক্যটি আনুষ্ঠানিকভাবে হ'ল floatদ্বিতীয় প্যারামিটারটিতে একটি অন্তর্নিহিত রূপান্তর রয়েছে floatযা এতে অন্তর্নিহিত রূপান্তর থেকে ভাল todouble

আসল ইস্যুটি হ'ল সংকলকটিকে একটি মধ্যবর্তী ব্যবহার করার অনুমতি দেওয়া হয়েছে (তবে প্রয়োজনীয় নয়) যা আনুষ্ঠানিক ধরণের (বিভাগ 11.2.2) এর চেয়ে উচ্চতর নির্ভুলতা । এজন্যই আপনি বিভিন্ন সিস্টেমে ভিন্ন আচরণ দেখতে পান: এক্সপ্রেশনটিতে (int)(6.2f * 10)সংকলকটিতে 6.2f * 10রূপান্তর করার আগে একটি উচ্চ নির্ভুল মধ্যবর্তী আকারে মান রাখার বিকল্প রয়েছে int। এটি যদি করে, তবে ফলাফলটি 61 হয়। এটি না হলে ফলাফল 62 হয় is

দ্বিতীয় উদাহরণে, floatপূর্ণসংখ্যাতে রূপান্তর হওয়ার আগে রাউন্ডিংয়ের জন্য স্পষ্ট স্পষ্ট নিয়োগটি বাধ্য করে।


6
আমি নিশ্চিত না যে এটি আসলে প্রশ্নের উত্তর দিয়েছে। কেন মান (int)(6.2f * 10)গ্রহণ করা হচ্ছে double, কারণ fএটি একটি float? আমি মনে করি মূল পয়েন্টটি (এখনও অনুत्तरযুক্ত) এখানে।
কেন 2 কে

1
আমার মনে হয় যে এটি সংকলক এটি করছে, কারণ এটি ভাসমান আক্ষরিক * int আক্ষরিক সংকলক সিদ্ধান্ত নিয়েছে যে এটি সেরা সংখ্যাসূচক প্রকারটি ব্যবহারের জন্য নিখরচায় এবং নির্ভুলতা বাঁচাতে এটি দ্বিগুণ হয়ে গেছে (সম্ভবত)। (এছাড়াও আইএল একই হ'ল ব্যাখ্যা করবে)
জর্জ ডেকেট

5
ভাল যুক্তি. ধরণ 6.2f * 10আসলে float, না double। আমি মনে করি যে সংকলকটি 11.1.6 এর শেষ অনুচ্ছেদে অনুমোদিত হিসাবে অন্তর্বর্তীটিকে অনুকূল করে তুলছে
রেমন্ড চেন

3
এর একই মান রয়েছে (মানটি 61.99999809265137)। পার্থক্য হ'ল মানটি পূর্ণসংখ্যায় পরিণত হওয়ার পথে নেয়। একটি ক্ষেত্রে, এটি সরাসরি কোনও পূর্ণসংখ্যার দিকে যায় এবং অন্য কোনও ক্ষেত্রে এটি floatপ্রথমে রূপান্তরের মধ্য দিয়ে যায় ।
রেমন্ড চেন

38
এখানে রেমন্ডের উত্তর অবশ্যই সম্পূর্ণ সঠিক। আমি নোট করেছি যে সি # সংকলক এবং জিট সংকলক উভয়ই যে কোনও সময় আরও নির্ভুলতা ব্যবহার করার অনুমতি দেয় এবং এটি অসঙ্গতভাবে করতে পারে । এবং বাস্তবে, তারা ঠিক তাই করে। এই প্রশ্নটি স্ট্যাকওভারফ্লোতে কয়েকবার এসেছে; সাম্প্রতিক উদাহরণের জন্য stackoverflow.com/questions/8795550/… দেখুন ।
এরিক লিপার্ট

11

বিবরণ

ভাসমান সংখ্যা খুব কমই সঠিক। 6.2fভালো কিছু হয় 6.1999998...। আপনি যদি এটি কোনও অন্তর্ভুক্ত করেন তবে এটি এটি কেটে যাবে এবং 61 * এ এই 10 টি ফলাফল।

জন স্কিটিস DoubleConverterক্লাস দেখুন। এই শ্রেণীর সাহায্যে আপনি সত্যিই স্ট্রিং হিসাবে ভাসমান সংখ্যার মানটি কল্পনা করতে পারেন। Doubleএবং floatউভয়ই ভাসমান সংখ্যা , দশমিক নয় (এটি একটি নির্দিষ্ট পয়েন্ট সংখ্যা)।

নমুনা

DoubleConverter.ToExactString((6.2f * 10))
// output 61.9999980926513671875

অধিক তথ্য


5

আইএল দেখুন:

IL_0000:  ldc.i4.s    3D              // speed1 = 61
IL_0002:  stloc.0
IL_0003:  ldc.r4      00 00 78 42     // tmp = 62.0f
IL_0008:  stloc.1
IL_0009:  ldloc.1
IL_000A:  conv.i4
IL_000B:  stloc.2

সংকলকটি তাদের ধ্রুবক মানের তুলনায় সংকলন-সময়ের ধ্রুবক অভিব্যক্তি হ্রাস করে এবং আমি মনে করি যে এটি কোনও স্থানে যখন ধ্রুবকে রূপান্তর করে তখন এটি একটি ভুল অনুমান করে int। এর ক্ষেত্রে speed2, এই রূপান্তরটি সংকলক দ্বারা নয়, সিএলআর দ্বারা তৈরি হয়েছে এবং তারা বিভিন্ন বিধি প্রয়োগ করে বলে মনে হচ্ছে ...


1

আমার অনুমান যে 6.2fভাসা স্পষ্টতা সঙ্গে বাস্তব উপস্থাপনা 6.1999999যখন 62fসম্ভবত অনুরূপ কিছু 62.00000001ingালাই(int) সর্বদা দশমিক মান ছাঁটাই করে যাতে আপনি সেই আচরণটি পান।

সম্পাদনা : মতামত অনুসারে আমি intকাস্টিংয়ের আচরণটি আরও সুনির্দিষ্ট সংজ্ঞাতে পুনরায় জমা করেছি।


একটি intকাটাতে দশমিক মান ছাঁটাই, এটি গোল হয় না।
জিম ডি'এঞ্জেলো

@ জেমস ডি'জেলো: দুঃখিত ইংরেজি আমার প্রাথমিক ভাষা নয়। সঠিক শব্দটি জানত না তাই আমি আচরণটিকে "পজিটিভ সংখ্যার সাথে সম্পর্কিত সময়ে গোলাকার ডাউন" হিসাবে সংজ্ঞা দিয়েছিলাম যা মূলত একই আচরণের বর্ণনা দেয়। তবে হ্যাঁ, পয়েন্টটি নেওয়া হয়েছে, কাটা কাটা এটির জন্য সঠিক শব্দ।
inbetween

কোনও সমস্যা নেই, এটি কেবল সিনট্যামিকস তবে কেউ যদি ভাবতে শুরু করে তবে সমস্যা সৃষ্টি করতে পারে float-> intরাউন্ডিং জড়িত। = ডি
জিম

1

আমি এই কোডটি সংকলন এবং সংযুক্ত করেছিলাম (উইন 7 /। নেট 4.0 এ)। আমি অনুমান করি যে সংকলক ভাসমান ধ্রুবক প্রকাশকে দ্বিগুণ হিসাবে মূল্যায়ন করে।

int speed1 = (int)(6.2f * 10);
   mov         dword ptr [rbp+8],3Dh       //result is precalculated (61)

float tmp = 6.2f * 10;
   movss       xmm0,dword ptr [000004E8h]  //precalculated (float format, xmm0=0x42780000 (62.0))
   movss       dword ptr [rbp+0Ch],xmm0 

int speed2 = (int)tmp;
   cvttss2si   eax,dword ptr [rbp+0Ch]     //instrunction converts float to Int32 (eax=62)
   mov         dword ptr [rbp+10h],eax 

0

Singleম্যান্টেইনগুলি কেবল 7 টি অঙ্ক করে এবং এটি সংকলকটিতে কাস্ট করার সময় Int32সমস্ত ভাসমান পয়েন্টের অঙ্কগুলি কেটে যায়। রূপান্তরকালে এক বা একাধিক উল্লেখযোগ্য সংখ্যা হারাতে পারে।

Int32 speed0 = (Int32)(6.2f * 100000000); 

619999980 এর ফলাফল দেয় সুতরাং (Int32) (6.2f * 10) 61 দেয়।

এটি আলাদা যখন দুটি একক গুণিত হয়, এক্ষেত্রে কোনও কাটা কাটা অপারেশন নেই তবে কেবল আনুমানিক হয়।

Http://msdn.microsoft.com/en-us/library/system.single.aspx দেখুন


-4

intপার্সিংয়ের পরিবর্তে আপনি টাইপ করা কাস্টিংয়ের কোনও কারণ আছে কি ?

int speed1 = (int)(6.2f * 10)

তাহলে পড়তে হবে

int speed1 = Int.Parse((6.2f * 10).ToString()); 

পার্থক্যটি সম্ভবত রাউন্ডিংয়ের সাথে করা: আপনি যদি কাস্ট করেন double তবে সম্ভবত 61.78426 এর মতো কিছু পাবেন।

নিম্নলিখিত আউটপুট নোট করুন

int speed1 = (int)(6.2f * 10);//61
double speed2 = (6.2f * 10);//61.9999980926514

যে কারণে আপনি বিভিন্ন মান পাচ্ছেন!


1
Int.Parseপ্যারামিটার হিসাবে একটি স্ট্রিং লাগে।
কেন 2 কে

আপনি কেবল স্ট্রিংগুলি পার্স করতে পারেন, আমার ধারণা আপনার অর্থ আপনি কেন সিস্টেম ব্যবহার করবেন না on রূপান্তর
ভিসি 74
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.