ورود

View Full Version : سوال: تبدیل نوع موقت



sourcecode
چهارشنبه 02 مهر 1393, 12:11 عصر
با سلام پس از اجرای برنامه ی زیر خروجی به صورت زیر به نمایش در میاد .
/* int_var1 : 1500000000
int_var2 : 211509811
int_var3 : 1500000000 */

مشکلم خط 10 و 13 هست که در خط 10 دستور | int_var=(1500000000*10)/10; | باعث میشه که جواب بدست آمده از محدوده int بیشتر بشه و جواب نادرست بده . حال در خط 13 دستور | int_var=(static_cast (1500000000*10) )/10; | باعث میشه که جواب درست نمایش داده بشه . سوالم اینه که چطوری خط 10 جواب اشتباه بدست میاد ولی در خط ۱۳ نه . مگه int و float هردو ۴بایتی نیستند ( البته محدودشون با هم تفاوت داره ) . شاید جوابش این باشه که محدوده اعدادی که float میگیره از int بیشتره , که باعث میشه داده ها از بین نره و جواب درست رو نشون بده . فک کنم؟!!؟

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
int int_var=150000000;
cout<<"\nint_var = "<<int_var;
int_var=(int_var*10)/10;
cout<<"\nint_var2 = "<<int_var;
int_var=1500000000;
int_var=(static_cast <float>(int_var)*10)/10;
cout<<"\nint_var3 = "<<int_var;
cout<<endl;
system("pause");
return 0;
}

rahnema1
چهارشنبه 02 مهر 1393, 12:27 عصر
اشتباه تاپیه اون دوتای اولی 7 تا صفر داره و آخری 8 تا صفر !

sourcecode
جمعه 04 مهر 1393, 10:24 صبح
اشتباه تاپیه اون دوتای اولی 7 تا صفر داره و آخری 8 تا صفر !
اصلاحش کردم .

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
int int_var=1500000000; //1,500,000,000
cout<<"\nint_var = "<<int_var;
int_var=(int_var*10)/10;
cout<<"\nint_var2 = "<<int_var; //wrong answer
int_var=1500000000;
int_var=(static_cast <float>(int_var)*10)/10;
cout<<"\nint_var3 = "<<int_var; //1,500,000,000
cout<<endl;
system("pause");
return 0;
}

omid_kma
جمعه 04 مهر 1393, 18:32 عصر
داخل float میشه حداکثر تا 2^24 +1رقم ذخیره کرد ( اندازه سایز mantissa) ( بدون از دست دادن اعشار) حالا اگر قسمت اعشاری مهم نباشه که این جا هم نیست میشه تا 2^32 رقم ذخیره کرد


داخل int هم کاملا بستگی به کامپایلر داره که چه رنجی رو میشه ذخیره کرد (مثلا برای من داخل visual studio و ویندوز 32 بیتی این هست 2^31 - 1 )
پس شما هم دارید از محدوده int میزنید بیرون هم از محدود float پس نتیجه ای که گرفتید درست نیست .
چون متغیر int_var از نوع signed هست overflow کردن متغیر undefine behavior هست .
پس چون خط 8 این جا undefine behavior هست پس ادامه برنامه هر اتفاقی ممکنه بیفته که یکی از این حالت ها ممکنه جواب صحیح باشه !
ضمنا اگر کد اسمبلی ;کدتون رو در حالت realease ّبینید متوجه میشید که اصلا این int_var ساخته نمیشه و ضرب وتبدیل ها هم اصلا انجام نمیشن
و صرفا این 3 مقدار push میشه داخل Stack(بخاطر همین این جا برنامه بدون ارور اجرا میشه ولی اگر مثلا این کد پیچیده تر بود واین oprimization ها انجام نمیشد چون متغیر داره overflow می کنه احتمالا برنامتون چون داشت فضای heap یا Stack رو خراب می کرد احتمالا ارور هم میداد )

_TEXT SEGMENT
_main PROC ; COMDAT

; 5 : int int_var = 1500000000; //1,500,000,000
; 6 : cout << "\nint_var = " << int_var;

push 1500000000 ; 59682f00H
push OFFSET ??_C@_0M@HNOEEJCC@?6int_var?5?$DN?5?$AA@
push DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits @D@std@@@1@A
call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostr eam@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
add esp, 8
mov ecx, eax
call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@s td@@QAEAAV01@H@Z

; 7 : int_var = (int_var * 10) / 10;
; 8 : cout << "\nint_var2 = " << int_var; //wrong answer

push 211509811 ; 0c9b6233H
push OFFSET ??_C@_0N@OMDEPKIM@?6int_var2?5?$DN?5?$AA@
push DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits @D@std@@@1@A
call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostr eam@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
add esp, 8
mov ecx, eax
call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@s td@@QAEAAV01@H@Z

; 9 : int_var = 1500000000;
; 10 : int_var = (static_cast <float>(int_var)* 10) / 10;
; 11 : cout << "\nint_var3 = " << int_var; //1,500,000,000

push 1500000000 ; 59682f00H
push OFFSET ??_C@_0N@NBFENDDM@?6int_var3?5?$DN?5?$AA@
push DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits @D@std@@@1@A
call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostr eam@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
add esp, 8
mov ecx, eax
call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@s td@@QAEAAV01@H@Z

; 12 : cout << endl;

mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits @D@std@@@1@A
push OFFSET ??$endl@DU?$char_traits@D@std@@@std@@YAAAV?$basic_ ostream@DU?$char_traits@D@std@@@0@AAV10@@Z ; std::endl<char,std::char_traits<char> >
call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@s td@@QAEAAV01@P6AAAV01@AAV01@@Z@Z

; 13 : system("pause");

push OFFSET ??_C@_05PDJBBECF@pause?$AA@
call DWORD PTR __imp__system
add esp, 4

; 14 : return 0;

xor eax, eax

; 15 : }

ret 0
_main ENDP
_TEXT ENDS



همون طور که میبنید برای حالت اول کامپایلر عدد 211509811 رو push کرده و حالت سوم مقدار درست رو که با توحه به undefine behavior بودن هر عدد دیگه ای جای این 2 عدد
هم میتونست بزاره .

rahnema1
جمعه 04 مهر 1393, 21:56 عصر
اون عدد قابل نمایش در float هست ولی قابل نمایش در int نیست
این لینک توضیح داده
http://en.wikipedia.org/wiki/Single-precision_floating-point_format

omid_kma
شنبه 05 مهر 1393, 00:16 صبح
اون عدد قابل نمایش در float هست ولی قابل نمایش در int نیست
این لینک توضیح داده
http://en.wikipedia.org/wiki/Single-precision_floating-point_format

میشه توضیح بدین چطوری 15*10^9 که تقریبا 2^34 هست داخل float با 32 بیت ذخیره میشه ؟؟ و دوباره به int تبدیل میشه ؟!
من ربطی هم بین این سوال و overflow کردن int , float و undefine behavior بودنش با اون لینک نمیبینم

داخل پاسخ بالا هم گفتم اگر یکم کد پیچیده تر بشه و optimize نشه ممکنه کلا جواب ها عوض میشه چون 2 تا undefine behavoiour داریم(یکی زمان تبدیل float به int یکی هم زمان ضرب int بزرگ در 10) (دلیلشو هم میتونین بصورت مفصل تر داخل این لینک هم ببینید http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer )

مثلا همون کد با یکم تغییر خروجیش داخل gcc اینه : http://ideone.com/vWQfLA
و داخل ویژوال استودیو هم تست کردم توی مد release اصلا چیزی چاپ هم نمیشه و برنامه بسته میشه بخاطر همون خراب شدن stack ولی توی مد دیباگ هر 2 تا عدد درست چاپ میشن.

rahnema1
شنبه 05 مهر 1393, 12:11 عصر
میشه توضیح بدین چطوری 15*10^9 که تقریبا 2^34 هست داخل float با 32 بیت ذخیره میشه ؟؟ و دوباره به int تبدیل میشه ؟!
من ربطی هم بین این سوال و overflow کردن int , float و undefine behavior بودنش با اون لینک نمیبینم

داخل پاسخ بالا هم گفتم اگر یکم کد پیچیده تر بشه و optimize نشه ممکنه کلا جواب ها عوض میشه چون 2 تا undefine behavoiour داریم(یکی زمان تبدیل float به int یکی هم زمان ضرب int بزرگ در 10) (دلیلشو هم میتونین بصورت مفصل تر داخل این لینک هم ببینید http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer )

مثلا همون کد با یکم تغییر خروجیش داخل gcc اینه : http://ideone.com/vWQfLA
و داخل ویژوال استودیو هم تست کردم توی مد release اصلا چیزی چاپ هم نمیشه و برنامه بسته میشه بخاطر همون خراب شدن stack ولی توی مد دیباگ هر 2 تا عدد درست چاپ میشن.


در ابتدا بهتره بحث در مورد این که آیا مثلا float یا int تعداد بیتهاشون 32 بیت هست یا نه را فعلا بذاریم کنار. ما همون پیاده سازی در کامپیوترهای رایج در نظر می گیریم گرچه استاندارد در زمینه اینکه پیاده سازی float چه طور باشه نظری نداده و واگذار کرده به پیاده سازی ها مختلف
فرض می گیریم که int و float هر کدوم 32 بیت اشغال می کنند و اعداد اعشاری طبق استاندارد ieee پیاده سازی شده اند
بیشترین عددی که float می تونه در خودش ذخیره کنه به صورت باینری میشه:
01111111011111111111111111111111
که معادل دهدهی میشه
340282346638528859811704183484516925440
که این تبدیل بر اساس همون لینکی هست که در بالا گذاشتم

حالا عدد یکی مانده به آخر میشه
01111111011111111111111111111110

که معادل دهدهی اون میشه
340282326356119256160033759537265639424

آیا تفاوت این دو عدد برابر 1 هست؟ خیر
تفاوتشون برابر با این عدده:
20282409603651670423947251286016

یعنی تمامی اعداد موجود در این دامنه را پوشش نمیده و خطای گرد شدن و .. را داریم

بنابراین در مورد 15000000000 که float باشه ما undefined behavior نداریم و همچنین overflow نداریم اما خطای گرد شدن و ... در اعداد اعشاری داریم

omid_kma
شنبه 05 مهر 1393, 12:58 عصر
یعنی شما از این صحبت ها می خواهید نتیجه بگیرید که عدد سومی که چاپ میشه undefine behaviour نیست ؟! و همیشه و همه جا همون عدد چاپ میشه ؟؟
ضمنا من هم جایی نگفتم overflow کردن float ا undefine behaviour هست بهتره یکبار دیگه دقیقتر پست بالا رو بخونید .


داخل پاسخ بالا هم گفتم اگر یکم کد پیچیده تر بشه و optimize نشه ممکنه کلا جواب ها عوض میشه چون 2 تا undefine behavoiour داریم(یکی زمان تبدیل float به int یکی هم زمان ضرب int بزرگ در 10


سوال کسی که تاپیک رو زده اینه : سوالم اینه که چطوری خط 10 جواب اشتباه بدست میاد ولی در خط ۱۳ نه
که من هنوزم ربطی بین اون لینک و خطای گرد شدن با چاپ شدن عدد صحیح در خط 13 نمیبینم ...

rahnema1
شنبه 05 مهر 1393, 13:21 عصر
یعنی شما از این صحبت ها می خواهید نتیجه بگیرید که عدد سومی که چاپ میشه undefine behaviour نیست ؟!

undefined هست در صورتیکه عدد اعشاری مذکور قابل نشان داده شدن در عدد صحیح مورد نظر نباشه
عدد اعشاری تقسیم بر 10 میشه در نتیجه قابل نمایش در int هست
حداکثر چیزی که می تونیم بگیم اینه که همون طور که قبلا گفتم implementation defined هست نه undefined

omid_kma
شنبه 05 مهر 1393, 21:11 عصر
undefined هست در صورتیکه عدد اعشاری مذکور قابل نشان داده شدن در عدد صحیح مورد نظر نباشه
عدد اعشاری تقسیم بر 10 میشه در نتیجه قابل نمایش در int هست
حداکثر چیزی که می تونیم بگیم اینه که همون طور که قبلا گفتم implementation defined هست نه undefined
آره حق با شماست اشتباه از من بود .
ذخیره اون مقدار در float نه overflow می کنه نه undefine هست
برگردوندن به int هم توی کد اول مشکلی نداره.