PDA

View Full Version : سوال: مقادیر اعشاری



hi.alir
سه شنبه 05 مرداد 1389, 10:50 صبح
الان داشتم مطالب قدیم رو چک می کردم، بعد متوجه شدم که مقادیر اعشاری مثل float و double ، دقیق نیستند.
می خواستم بدونم که با این مشکل باید چی کار کرد؟

yaseriran
سه شنبه 05 مرداد 1389, 11:50 صبح
درود!

double در c تا 6 رقم درست می ده و در ++c تا 3 رقم درست.
float در c تا 4 رقم درست می ده و در ++c تا 3 رقم.

یکی از راهها برای دقیق محاسبه کردن، استفاده از double در c است.
البته از long double هم می شود در ++c استفاده کرد.

باید راههای دیگه هم وجود داشته باشه.

پیروز باشید...


برای بدست آوردن این دنیا، علم و برای بدست آوردن آن دنیا، تقوی... بوعلی سینا

Salar Ashgi
سه شنبه 05 مرداد 1389, 13:04 عصر
این در واقع یه طبیعت واسه نوع های اعشاریست و زیاد کاریش نمیشه کرد !

ولی خوب گاها میشه با عملیات Casting و گرد کردن و ... دقت رو کنترل کرد .

موفق باشید ./

PC2st
سه شنبه 05 مرداد 1389, 14:53 عصر
دقت float تا ۷ رقم و دقت double تا ۱۵ رقم است.
یعنی اگر بخواهید ارقامی بیشتر از ۷ رقم را از float بخواهید، سایر ارقام اضافی، دقیق نخواهند بود.

مثلا عدد ۹٫۸ را در متغیری از نوع float ذخیره کرده‌اید، این متغیر تا ۷ رقم را دقیق است پس اگر شما توسط تابع printf بخواهید تا ۱۰ رقم از این متغیر را بگیرید، نتیجهٔ زیر حاصل خواهد شد:


9.8000001907
1907 در خارج از محدودهٔ ۷ رقم دقت قرار دارد، پس صفر نبوده و دقیق نیست.

به همین خاطر، انواع float و double تنها در بازه‌ای از ارقام، دقیق هستند...
... و نباید بطور مستقیم از عملگرهای == و =! برای کنترل مقادیر آنها استفاده کنید.

hi.alir
سه شنبه 05 مرداد 1389, 20:12 عصر
جالبه.
طبق تست های من، تو کد زیر

float f = 0.0f;

f += 0.5f; f += 0.2f;

cout << f << endl;

getch();
return 0;
حاصل تو watch شده 0.69999999 ولی 0.7 چاپ می کنه :متفکر:
معمولا تو محاسبات 0.0000001 اشتباه داره.
هیچ جوره نمیشه این اشتباهات رو درست کرد؟
نکته ی جالب دیگه این بود که تا f += 0.5 درست کار می کنه، ولی تو محاسبه ی بعدی، خطا داره!
double هم که امتحان کردم، خطاش بیشتر از float بود!

PC2st
سه شنبه 05 مرداد 1389, 22:57 عصر
با اینکه گفته می‌شود نوع float تا ۷ رقم دقت دارد، ولی اثر سایر ارقام را نمی‌توان در نظر نگرفت. نوع float تا ۷ رقم دقت دارد اما در رقم هشتم اشتباه می‌کند. پس همواره به اندازهٔ 0.000000n (عدد n در رقم هشتم قرار دارد) به عدد واقعی اضافه، یا از آن کم می‌شود. یعنی عدد 0.7 می‌توانست به جای 0.6999999 برابر با 0.7000001 باشد. یعنی عدد 0.7 در متغیری از نوع float در یکی از ۲ حالت زیر ذخیره می‌شود:


0.700000 + 0.000000xxxxx...
0.700000 - 0.000000xxxxx...
x می‌تواند هر عددی باشد و علامت سه‌نقطه یعنی می‌تواند تا بینهایت x وجود داشته باشد. اما چیزی که مهم است این است که همواره ۷ رقم اول به درستی ذخیره می‌شوند. دلیل اضافه یا کم شدن 0.000000xxx از مقدار واقعی، به خاطر تقریبی‌بودن روش ذخیره مقادیر ممیز شناور در حافظه است (یعنی در حقیقت برای ذخیره کردن مقادیر ممیز شناور (اعشاری) چیزی کم یا اضافه نمی‌شود بلکه این نحوهٔ ذخیره‌شدن است که باعث بوجود آمدن چنین تقریبی شده و از ارقام هشتم به بعد از مقدار واقعی را درست ذخیره نمی‌کند).

درست است که 0.7 بصورت 0.699999 ذخیره می‌شود اما در مقایسهٔ زیر:


if(0.7F > 0.699999F)
{
//...
}
همواره شرط درست خواهد بود. تنها نباید از عملگرهای == و =! برای مقایسه استفاده کرد.

برای درک تقریبی بودن مقادیر موجود در انواع float و double اعداد گنگ را در نظر بگیرید (مثل عدد pi) که هیچگاه بشر نتوانسته مقدار دقیقی برای آن درنظر بگیرد. تا بینهایت هم که ارقام عدد pi محاسبه شود، اما بازهم به مقدار واقعی عدد pi نخواهند رسید و به جای آن، در محاسبات برای ساده‌سازی، عدد pi را بصورت 3.1416 تقریب می‌زنند و سایر ارقام را صرف نظر می‌کنند.

بطور مثال در نوع double که تا ۱۵ رقم دقت دارد، ارقام شانزدهم به بعد نادرست هستند، پس مقدار ذخیره شده در این نوع، همواره به اندازهٔ 0.00000000000000n (عدد n در رقم شانزدهم قرار دارد می‌توان این مقدار را صفر در نظر گرفت چون بسیار ناچیز است) نسبت به مقدار واقعی کمتر یا بیشتر است. به نظر من که نوع double تقریباً برای همهٔ محاسبات کافی است.

hi.alir
چهارشنبه 06 مرداد 1389, 10:05 صبح
تشکر.
پس اگر از رقم 6 به بعد تو اعشار خطا داره، پس واسه چی اصلا اونجا رو گذاشتند؟ یه float که برابر 0 هست رو اینجوری نشون میده 0.00000000! میشه 8 رقم اعشار! double هم 17 رقم اعشار!

واسه CPU از نظر سرعت فرقی بین == و >، یا != و == یا .... هست؟

سوال اخرم اینه که، چرا بعد از مقادیر float یه f میزارند؟ یا چرا 0 رو به شکل 0.0 یا 0.0f می نویسند؟

PC2st
چهارشنبه 06 مرداد 1389, 11:00 صبح
در نوع float از رقم ۸ به بعد خطا دارد، به خاطر ماهیت و نحوهٔ ذخیره‌سازی اعداد اعشاری در حافظه و به خاطر محدودیت تعداد بیت‌ها برای ذخیره‌سازی، نمی‌توان همهٔ ارقام را به درستی ذخیره کرد پس نتیجهٔ آن تقریبی است.


یه float که برابر 0 هست رو اینجوری نشون میده 0.00000000! میشه 8 رقم اعشار! double هم 17 رقم اعشار!مقادیر ممیز شناور در اعداد صحیح کاملا درست هستند، مثلا اگر عدد ۹۸ را در متغیری از نوع float ذخیره کنیم، دقیقاً همان عدد ۹۸ ذخیره می‌شود و اگر تا صدها رقم اعشار را هم بگیریم، بصورت 98.00000000000000 گرفته می‌شود. اما اگر همین عدد را با یک ممیز ذخیره کنیم، یعنی عدد ۹٫۸ را در متغیری از نوع float ذخیره کنیم، این مقدار به خاطر وجود اعشار تاحدودی بسیار ناچیز با مقدار اصلی تفاوت خواهد داشت و اگر تا چند رقم را از آن بگیریم، بصورت 9.8000001907 گرفته می‌شود که دقیقاً معادل 9.8 نیست.

بنابراین بطور کلی، اعداد صحیح همانطور که هستند توسط نوع float کاملا به درستی ذخیره می‌شوند اما اعداد اعشاری توسط نوع float با خطای تقریب ناچیز و قابل صرف نظر، ذخیره می‌شوند.


float a = 98.0F; //98.0000000000000000
float b = 9.8F; //9.8000001907

واسه CPU از نظر سرعت فرقی بین == و >، یا != و == یا .... هست؟اگر بخواهیم خیلی سخت‌گیرانه باشیم، احتمالاً سرعت < و > کمتر از == و != است و احتمالاً سرعت =< و => کمتر از بقیه است (البته اگر چنین تقاوت سرعتی هم وجود داشته باشد، آنقدر ناچیز و آنقدر بی‌تاثیر است که به هیچ‌وجه تاثیرگذار نیست).


سوال اخرم اینه که، چرا بعد از مقادیر float یه f میزارند؟ یا چرا 0 رو به شکل 0.0 یا 0.0f می نویسند؟ چون مقادیر ممیز شناور، بطور پیش‌فرض double هستند. برای جلوگیری از یک cast اضافه، آن را بطور مستقیم از نوع float در نظر می‌گیرند. هر چند که اکثر کامپایلرها هوشمندند و این cast اضافه را انجام نمی‌دهند. اما ممکن است، مقدار 1.22222222223335 را بخواهیم در نوع float ذخیره کنیم، در اینصورت ارقام آخر یعنی 22223335 صرف نظر می‌شوند و کامپایلر یک اخطار را مبنی بر تاثیر ناشی از آن را به برنامه‌نویس هشدار می‌دهد.

...StacK...
چهارشنبه 06 مرداد 1389, 11:06 صبح
0 رو واسه من 7 رقم بیشتر نشون نمیده:


#include "stdafx.h"
#include<iostream>
using namespace std;
int main()
{
float x=0;
cout<<x<<endl;
printf("%f",x);

}

خروجی:

0
0.000000

cpu کدهای ماشین رو اجرا میکنه و قطعه بین == و !=و.. از نظر خروجی کامپایلر به کد ماشین تفاوت هست اما در سرعت اجرا خیر.


f نشانه ممیز شناور بودن عدد هست,میتونی نزاری.

در ضمن میتونی حوزه نمایش متغیر رو تغییر بدی که مثلا چند رقم عدد اعشاری و چند رقم صحیح نشون بده.

مثال:

printf("%6.2f",x);

در اینجا کل ارقام + "." 6 کاراکتر و 2 رقم هم برای اعشار در نظر گرفته میشه.

یعنی 3 رقم برای قسمت صحیح و یک رقم برای"." و 2 رقم برای اعشار.


کلا خودتو خسته ی این موارد نکن -صبر کن هر موقع در برنامه نویسی بهش احتیاج پیدا کردی به رفرنس ها و مستندات کامپایلر رجوع کن.

PC2st
چهارشنبه 06 مرداد 1389, 11:52 صبح
اگر بخواهیم خیلی سخت‌گیرانه باشیم، احتمالاً سرعت < و > بیشتر از == و != استاین را اشتباه نوشته بودم...
با در نظر گیری signed و unsigned شاید بتوان گفت‌ سرعت < و > کمتر از == و =! است و با این دوست عزیز موافقم، در عمل این تفاوت سرعت وجود ندارد.

rahnama66
پنج شنبه 10 فروردین 1391, 10:34 صبح
من با مقادیر اعشاری در C# هم مشکل دارم. من یک فیلد دارم از نوع Double در دیتابیسم(اکسس). این مقادیر حداکثر 2 رقم اعشار دارد ولی وفتی با هم جمع می کنم کلی رقم اعشار اضافه پبدا میکند.
این کد موجود در برنامه من هست

if (DtMandeh.Rows.Count != 0)
{
j = DtMandeh.Rows.Count;
for (i = 0; i < j; i++)
{
mandehvaziat = Convert.ToString(DtMandeh.Rows[i][6]);
mandehrialit = Convert.ToInt32(DtMandeh.Rows[i][2]);
mandehtalat = Convert.ToDouble(DtMandeh.Rows[i][3]);

listBox1.Items.Add(mandehtalat);
if (mandehvaziat == "بدهکار")
{
summandehr = summandehr + mandehrialit;
summandeht = summandeht + mandehtalat;
}

if (mandehvaziat == "بستانکار")
{
summandehr = summandehr - mandehrialit;
summandeht = summandeht - mandehtalat;
}
listBox1.Items.Add(mandehvaziat);
listBox1.Items.Add(summandeht);
}
اما مقادیر چاپ شده در listbox مقادیر ذیل است.
http://130.0.img98.net/out.php/i266577_listbox.jpg

الان جمع تمامی گزارشاتم با این وجود اشتباهه
اگر ممکنه راه حلی بهم پیشنهاد کنید.