PDA

View Full Version : مشکل با این ارور: floating point overlow



arslan tegin ghazi
جمعه 16 تیر 1391, 13:31 عصر
با سلام و عرض خسته نباشید خدمت بر و بچه های برنامه نویس

من تا حالا این ارور "floating point overlow" رو ندیده بودم شدید رفته رو مخم چیکارش کنم این ارور در تقسیم دو تا عدد به هم پیش مییاد .


توضیح در مورد چیزی که می خوام بنویسم :

یک کتابخونه از اینترنت پیدا کردم که توش تابع بسل و توابع کار با اعداد مختلط هستش می خوام یه رابطه ی دو متغیره رو بصورت عددی حل کنم این ارور در تابع تقسیم دو عدد مختلط پیش میاد .

لینک کیتابخونه ها (http://jean-pierre.moreau.pagesperso-orange.fr/p_bessel.html)

کد خودمم لازم بود بگید بذارم .

خیلی ممنون .

arslan tegin ghazi
جمعه 16 تیر 1391, 15:14 عصر
باید اضافه کنم پیدا کردم چرا این طوری میشه ( مخرج عجیب کوچولو میشه در حد : 9.58574652707027E-304 :متعجب: ) خب حالا چیکار کنم مشکل حل بشه به محاسباتم هم آسیبی نرسه ؟

جون من اینجا 2 3 تا خوره برنامه نویسی پیدا نمیشه مشکل من رو حل کنه ؟ :متفکر:

SayeyeZohor
جمعه 16 تیر 1391, 17:25 عصر
سورستو بذار يك نگاهي بندازيم
شايد بتونم كمكت كنم:قلب:

BORHAN TEC
جمعه 16 تیر 1391, 17:35 عصر
برای رفع این مشکل باید نوع داده متغیر خود را به یک نوع با محدوده بزرگتر تغییر دهید. به عنوان مثال اگر از کد زیر استفاده کنید با همان خطای floating point overlow مواجه خواهید شد:
procedure TForm1.Button1Click(Sender: TObject);
var
a: single;
begin
a:= 10312312312312311212;
a := a + 10312312312312311212;
a := a / 0.00000000000000000000001;
end;

که برای رفع خطای مذکور می توانید نوع متغیر را به Extended تغییر دهید:
procedure TForm1.Button1Click(Sender: TObject);
var
a: Extended;
begin
a:= 10312312312312311212;
a := a + 10312312312312311212;
a := a / 0.00000000000000000000001;
end;

arslan tegin ghazi
جمعه 16 تیر 1391, 18:09 عصر
با تشکر از هر دو عزیز روشی که شاهین خان گفتن رو خودم چک کردم یه ارور دیگه داد که نتونستم حلش کنم : floating point invalid operation

این ارور رو توی تابع بسل میده که از لینکی که گذاشتم کپی کردم ( تابع بسل نوع I برای اعداد مختلط ) . رو این سطر :
ARG := (FNU-1.0*INU)*PI;
در حالی که مقادیر رو این طوری نشون میده :
ARG=-3.72036854775808e+1319
FNU=0
INU=0
PI=-3.72036854775808e+162

دیگه با این ارور و این مقادیر که برای متغیر ها آورده من :|

arslan tegin ghazi
جمعه 16 تیر 1391, 18:16 عصر
دوستی که سورس خواستن یه آدرس ای-میل بدن بفرستم ، خیلی با اینجا گذاشتن حال نمی کنم :لبخند:

arslan tegin ghazi
جمعه 16 تیر 1391, 18:25 عصر
چطور میشه بگیم این ارور رو بی خیال بشه ؟ یعنی عیب نداره یه قسمتی از اطلاعات از دست بره ولی برنامه به اجراش ادامه بده .

SayeyeZohor
جمعه 16 تیر 1391, 18:50 عصر
M_Nikoei2005@Hotmail.com
سعي ميكنم كمكتون كنم

arslan tegin ghazi
جمعه 16 تیر 1391, 20:04 عصر
خیلی ممنون که وقت میذارید فرستادم یه بار مجدد buildش کنید .

Ananas
جمعه 16 تیر 1391, 20:18 عصر
سلام.

من تا حالا این ارور "floating point overlow" رو ندیده بودم شدید رفته رو مخم چیکارش کنم این ارور در تقسیم دو تا عدد به هم پیش مییاد .
:شیطان: عوضش تا دلت بخواد من این ارور رو دیدم. یعنی غیر ازین ارور چیز دیگه ای از ارورا یادم نمیاد. چرا ... چرا ... وایسا .. Division by zero! اینم هست که خدا دوتاشونو لعنت کنه که دشمن محاسباتن.
شما که می دونی چه مواقعی اعدادت اینقدر بزرگ میشن و یا اینقدر کوچیک میشن با یک if مناسب قبل از توابع ضرب و تقسیم و توان و امثالهم اون ها رو به +INF و یا -INF مقدار بده. مثال :

const
MAX_SINGLE_E = 35;
MAX_EXTENDED_E = 4900;
var
x, y, z : Single;
begin
y := 0;//1.0e0;
x := 1.0e39;
if (y = 0) or ((Log10(Abs(x)) - Log10(Abs(y))) >= MAX_SINGLE_E) then
z := Infinity
else
z := x / y;
// ---------------------------
x := 1.0e-10;
y := 1.0e-39;
if Abs(Log10(Abs(x)) + Log10(Abs(y))) >= MAX_SINGLE_E then
begin
if Abs(x) > 1 then
z := Infinity
else
z := 0.0;
end
else
z := x * y;
// ---------------------------
x := 1.0e2;
y := -1.0e2;
if Abs(Log10(Abs(x)) * y) >= MAX_SINGLE_E then
begin
// ???
end
else
z := Power(x, y);
ShowMessage(FloatToStr(z));
end;

فکر میکنم درست باشه. امتحانش کن. برای توان حوصلم نیومد فکر کنم تو حالت های مختلف چه مقداری از بینهایت، منهای بینهایت و یا 0 رو باید داد.
اگه خیلی خواستی محکم کاری کنی این هم بد نیست :

var
x, y, z : Extended;
begin
y := 1.0e-30000;
x := 1.0e0;
if (Abs(x) < 1e4000) and
(Abs(y) < 1e4000) and
(Abs(x) > 1e-4000) and
(Abs(y) > 1e-4000) and
((Log10(Abs(x)) - Log10(Abs(y))) >= MAX_EXTENDED_E) then
z := x / y
else
z := Infinity;
ShowMessage(FloatToStr(z));

برای بقیه هم به همین ترتیب.
این خطا بدلیل اینه که توان 10 اعداد (همون عدد بعد از e که تو نوشتن به نماد علمی به کار میره) تو متغیر های ما یک مقدار محدودی رو می پذیره مثلا برای Single چیزی حدود 1.0e38 به بالا با خطا مواجه میشه و برای Extended این مقداد تا حدود 1.0e4900 البته اعدادی که نوشتم دقیق نیست تقریب به سمت احتیاط هست (یعنی یکم کوچیک تر گرفتم شایدم خیلی:لبخند:). شما سعی کن مثلا برای Extended رنج اعدادت رو بین مقادیر 1.0e-4900 , 1.0e4900 نگه داری. تو یونیت Math مقادیر MaxExtended و MaxSingle رو ببین.

SAASTN
شنبه 17 تیر 1391, 00:30 صبح
if (y = 0) or ((Log10(Abs(x)) - Log10(Abs(y))) >= MAX_SINGLE_E) then
تصور می کنم هدف این بخش پیدا کردن صفر و مقادیر بسیار نزدیک به صفر هست که باعث بروز خطا در محاسبات می شن. روش دیگه برای پیدا کردن این مقادیر استفاده از کد زیره:
if Abs(y) < Epsilon then
حالا مقدار Epsilon خودش بستگی به جنس محاسبات داره. مثلا توی رشته عمران خودشون می گن که دقت برابر یه خیز آهو هست! اما چون پایه محاسبات ما در مورد مقادیر از جنس طول برابر 1 میلیمتره ما دقت رو هم برابر همون یک میلیمتر می گیریم. یعنی اگر مقداری از یک کمتر باشه، محاسباتش شدیدا تحت بررسی قرار می گیره! و همینطور اگر اختلاف دو مقدار کمتر از یک باشه برابر فرض میشن. البته باید توجه کرد که هر جنس مقداری اپسیلون خودش رو داره و شما نمی تونی برای مقایسه دو مقدار از جنس نیرو از اپسیلون طول استفاده کنی. حالا شما هم باید یه اپسیلون مناسب برای محاسبات خودت پیدا کنی، این مقدار بعضی وقتا دقیقا با یه استدلال ریاضی بدست میاد و بعضی وقتا هم می تونه بصورت تجربی بدست بیاد.
در هر صورت در یک فراید محاسباتی ریاضی استفاده از عملگر = یا <> برای مقایسه مقادیر کاملا اشتباه هست و برای مقایسه باید از حالت های حدی استفاده کرد. حتی بعضی وقتها > یا < هم خطاهایی در محاسبات ایجاد می کنن، یعنی بعضی وقتها منظور ما از این که بگیم a از b بزرگتره، این هست که a حداقل به میزان اپسیلون از b بزرگتره.

چطور میشه بگیم این ارور رو بی خیال بشه ؟ یعنی عیب نداره یه قسمتی از اطلاعات از دست بره ولی برنامه به اجراش ادامه بده .
می تونی از بلوک try .. except استفاده کنی تا خطاهای مورد نظرت رو خودت مدیریت کنی. اما اصلا پیشنهاد نمی کنم که توی یه فرایند محاسباتی این کار رو بکنی، بهتره خودت شرایط وقوع خطا رو تشخیص بدی و در اون شرایط محاسبات رو به شکل درستش ادامه بدی.

Ananas
شنبه 17 تیر 1391, 01:33 صبح
if (y = 0) or ((Log10(Abs(x)) - Log10(Abs(y))) >= MAX_SINGLE_E) then


تصور می کنم هدف این بخش پیدا کردن صفر و مقادیر بسیار نزدیک به صفر هست که باعث بروز خطا در محاسبات می شن.

اگه بخوایم دقیق تر بگیم هدفش اینه که می خواد ببینه اگه این دو عدد به هم تقسیم بشن عدد حاصل چند رقم با اعشار فاصله می گیره یعنی همون 38 در 1.0e38 و اگه فاصله بیشتر از چیزی باشه که متغیر ما می تونه ذخیره کنه باشه، از انجام تقسیم صرف نظر میکنه و یک مقدار دستی رو جایگزین میکنه.
مثلا برای ضرب در نظر بگیرید که دو تا عدد داریم، حالا ارقامشون رو نخوایم لحاظ کنیم و فقط بگیم چند رقم با اعشار فاصله دارن (یعنی مثلا بگیم 5.165165165e23 تقریبا هم رده هست با 1.0e23) بعد بیایم بگیم اگه 1e23 ضرب بشه در 1e18 حاصل میشه چه عددی؟ بدون محاصبه مشخصه که ما باید توان 10 ها رو با هم جمع کنیم. پس برای میایم می گیم 23 + 18 از 38 بزرگ تر هست پس در نتیجه در عمل ضرب احتمال خطر وجود داره. ازش میگذریم :

if Abs(Log10(Abs(x)) + Log10(Abs(y))) >= MAX_SINGLE_E then
//...

برای توان مطمئن نیستم درست نوشته باشم ولی فکر میکنم درست باشه. یک عددی پایه داره به اندازه ی عدد توان در خودش ضرب میشه پس توان 10 عدد پایه رو در عدد توان ضرب می کنیم :

if Abs(Log10(Abs(x)) * y) >= MAX_SINGLE_E then
//...

اما فرق این روش با روش Epsilon چیه؟ روش Epsilon خوبه ها ولی ما می خوایم دامنه ی عملکرد رو افزایش بدیم. ببینید برای محاسبات شما چه Epsilon ای لحاظ میکنید؟ همونطور که خودتون گفتید بستگی به نوع محاسبات داره ولی ما بیشترین دقت رو می خوایم! یعنی هر چی جا داره! اگه epsilon عددی کوچیک باشه برای تقسیم خیلی بزرگ به خیلی کوچیک اشتباه رخ میده و اگه epsilon رو یزرگ تر بگیرید برای اعداد کوچیک دیگه نمیشه محاسبات انجام داد. مثال :

x := 1e-14;
y := 1e-15;
// x / y = 10 عدد بزرگی نیست باید بتونیم تو محاسباتمون داشته باشیم.
Epsilon := 0.1; // ? ... نه .
Epsilon := 0.0001; // ? ... بازم ته .
Epsilon := 0.0000000000000000001; // ? ... جواب میده .
x := 1e30; // دیگه جواب نمیده
y := 1e-15;//
Epsilon := ?

اینجاست که به یک Epsilon پویا احتیاج پیدا می کنیم تا دامنه ی عملکزدش وسیع تر بشه و خودش بتونه اتوماتیکی تشخیص بده که برای دو تا عدد مذکور کدوم Epsilon کافیه و روشی مشابه Log10 رو بکار میگیریم.

SAASTN
شنبه 17 تیر 1391, 23:12 عصر
بحثی نیست، بدون شک روشی که شما ارائه کردی دقت رو در بازه بزرگتری از اعداد تضمین میکنه. اما من با توجه پست شماره 5 فرض رو بر این گذاشتم که خطا بخاطر ایجاد اعداد بسیار کوچیک اتفاق میافته.

ولی ما بیشترین دقت رو می خوایم! یعنی هر چی جا داره!
همیشه اینطور نیست، در اکثر پردازشهای ریاضی دو فاکتور متقابل برای ما اهمیت دارند، صحت محاسبات و زمان انجام محاسبات. دقت به نوعی ضامن صحت محاسباته و با توجه به جنس و هدف محاسبات ممکنه تعاریف متفاوتی داشته باشه. مثلا ممکنه صورت مسئله شبیه سازی یه انفجار هسته ای برای تست یه قطعه در راکتور باشه، اینجا نتیجه صحیح نتیجه ایه که بیشترین دقت ممکن رو داشته باشه، پس زمان انجام عملیات میره در درجه دوم و ممکنه سریع ترین سرور پردازشی جهان رو یک ماه زیر بار بزاریم تا به این دقت برسیم. اما یه موقع صورت مسئله decode کردن یه فیلم یا پردازش های RealTime یه بازی باشه، در اینجا دقت به راحتی فدای سرعت میشه، اما حتی اینجا هم دقت رو تا هرجایی که بخوایم نمی تونیم بیاریم پائین و ممکنه FrameRate پائین تر رو به یه ترسیم بد ترجیح بدیم. همین که بالاخره یک جایی محاسبات رو متوقف می کنیم و مقدار INF رو در خروجی قرار می دیم به این معنیه که دقت هم تا یه جایی برای ما اهمیت داره، وگرنه از روش های دیگه برای بدست آوردن جواب حقیقی استفاده می کردیم. از طرفی محاسباتی که در اونها هم اعداد خیلی بزرگ و هم اعداد خیلی کوچیک شرکت دارن ذاتا محاسبات دقیقی نیستن، اگر واقعا توی چنین محاسباتی نیاز به دقت بالا داشته باشیم خیلی وقتا مجبوریم با تغییر متغیر یا روشای دیگه شکل محاسبات رو تغییر بدیم تا بازه اعداد محدود تر بشن.
اما اینکه دقت محاسبات رو چطور تعیین کنیم، همونطور که قبلا هم گفتم، جنس محاسبات تعیین کننده هست. اگر مسئله تنها یه مسئله محض عددی باشه، دقت رو باید با یه استدلال ریاضی بدست بیاریم. اما در مورد محاسبات کاربردی که با داده های حقیقی کار می کنن، معمولا به دقت بالایی احتیاج نداریم. حتما از آز فیزیک یک یادتون میاد که وقتی متر ما طول رو به میلیمتر اندازه می گیره، اگه ما محیط یه دایره به شعاع 5 میل رو 78.539 به دست بیاریم نمرمون برابر -0- میشه! دقت مورد نیاز برای بدست آوردن حجم یه قطعه مکانیکی موتور با دقت بدست آوردن حجم یه میدان نفتی برابر نیست و مثلا ما نمی تونیم بگیم حجم هر دو رو به میکرومتر مربع می خوایم. مطمئن باشید که الگوریتم های مورد استفاده در این دو مسئله هم با هم تفاوت های بزرگی دارند، چرا؟ به خاطر همون مسئله زمان انجام محاسبات، لزومی نداره که ما محاسبات بدست آوردن حجم اون میدان رو ده برابر کندتر کنیم تا جواب رو به میلیمتر مربع بدست بیاریم، حتی متر مربع هم دقت خیلی بالایی به حساب میاد.
من روش شما رو با روش خودم با این کد تست کردم:
const
Count: Cardinal = 100000000;
var
x, y: Double;
r: Boolean;
Ticks, I: Cardinal;
begin
x := Random;
y := Random;
Ticks := GetTickCount;
for I := 0 to Count - 1 do
r := Abs(Log10(Abs(x)) + Log10(Abs(y))) >= 38;
Caption := IntToStr(GetTickCount - Ticks);
Ticks := GetTickCount;
for I := 0 to Count - 1 do
r := Abs(y - x) < 1e-10;
Caption := Caption + ' ' + IntToStr(GetTickCount - Ticks);
end;

جواب روی سیستم من 13619 در برابر 484 میلی ثانیه بود. یعنی روش اول بیشتر از 28 برابر کندتره. حالا اگر در محاسبات فقط اعداد خیلی کوچیک شرکت داشته باشن و حجم محاسبات هم خیلی بالا باشه شاید بتونیم به این نتیجه برسیم که نیازی به دقت بیشتر از 20 یا 30 رقم اعشار نداریم.
با این حال هر دوی روش ها باید درست و به جای خودشون استفاده بشن. درواقع ما وقتی می خوایم یه محاسبه عددی رو بصورت کامپیوتری انجام بدیم باید به ریاضیه صورت مسئله اشراف داشته باشیم، بازه ورودی ها و حداقل دقت لازم خروجی رو بشناسیم، از طرفی به نحوه عملکرد روشهایی که به کار می بریم هم مسلط باشیم. اینطور نباشه که با 1000 بار پردازش دقت ما هم 1000 بار کاهش پیدا کنه. مثلا تو همون مثال محیط دایره اگر ما گرد کردن رو روی Pi انجام بدیم یا روی نتیجه نهایی، جوابهای متفاوتی بدست میاریم و اگه قرار باشه خود این محیط، ورودی یه تابع دیگه باشه ممکنه در نتیجه اون تابع تفاوتهای بزرگتری بوجود بیاد. آقا کلا اینکه کار علمی رو باید به روش علمی انجام داد و هر مسئله علمی راه حل خودش رو می طلبه.

Ananas
یک شنبه 18 تیر 1391, 14:54 عصر
کاملا موافقم. در بعضی موارد سرعت مهم تره.

جواب روی سیستم من 13619 در برابر 484 میلی ثانیه بود.
بالاخره لگاریتم عمل پر هزینه ایه.
معمولا جایی که سرعت لازمه دقت فدا میشه مثل بازیها. ولی کد لگاریتمی که نوشتم فقط باید در نقاط بحرانی انجام داد نه تو همه ی ضرب و تقسیم ها. یعنی جایی که می دونیم قراره خطا رخ بده که خوب برای مثال زدن به طور کامل نوشتم که هیچ جا خطا رخ نده و مثال برای طریقه مهار خطا تقریبا کامل باشه. مثلا اگه ما بدونیم که اعداد ما به سمت صفر هستن و یا مثلا حول مثبت بینهایت تغییر میکنن مشخصا برای همون قسمت if لازم رو می نویسیم و خوب این که می بگیم :

یعنی روش اول بیشتر از 28 برابر کندتره.
درسته ولی ما تو محاسباتمون موارد دیگه ای هم داریم و که در کل باید سرعت ها مقایسه بشن نه موضعی. کلا عمل تقسیم هم نسبت به ضرب سنگین تره مثلا ضرب در نیم بهتر از تقسیم به 2 هست با اینکه نتیجه یکی میشه حالا چه برسه به لگاریتم. ولی من هم مثل شما روش اول رو برای محاسبات ریل تایم و سریع پیشنهاد نمیکنم. یه مواقعی ما میگیم سرعت هر چی باشه مهم نیست ولی عددی که لازم داریم باید به هر قیمتی که بشه صحیح به دست بیاد.
البته روش شما کاملا صحیحه و منظور من فقط این بود که خاصیت انواع اعشاری تو کامپیوتر رو بهش دقت کنیم و رفتار اونها رو تو دو تا روش با هم مقایسه کنیم و در مواقع لازم، از هر کدوم از روش ها می تونیم بسته به موقعیت استفاده کنیم.
من برای بدست آوردن توان 10 نماد علمی روش خوبی رو اجرا نکردم اگه روش و تابع بهتری میشناسید لطفا بگید. در واقع log10 رو لازم نداریم اونم با اعشار دقیق و فقط می خوام حدود توان 10 رو بگیرم.