وقتی از تابعی که زحمت کشیدید و نوشتیم استفاده میکنیم اگر مقدار گام رو 25 قرار بدیم
عدد 1237 چرا میشه 1225 در صورتی که قرار بود بشه 1250.
مشکل کجاست.
دلیلش این بود که 25 عدد فرد هست و حاصل تقسیمش بر دو یک عدد اعشاری میشه ( 12.5 ) ، در حالی که باقیمانده ی تقسیم 1237 بر 25 میشه 12 و در اینصورت شرط برقرار نمیشه ، تصحیح شد :
ببخشید به نظرم کل مسیری که این تاپیک طی کرده یکم مشکل داره، اگه اجازه بدید یکم افاضات ریاضی بکنم.
ببینید وقتی ما میگیم "گرد کردن" یا "رند کردن" خوب منظور یه مفهموم ریاضیه موجوده، ما یه گرد گردن داریم که معنیش اینه که یه عدد اعشاری رو می خوایم به ترتیبی به یک عدد صحیح تبدیل کنیم که اگه قسمت اعشاری اون به صفر نزدیکتر بود، حاصل قسمت صحیح ورودی باشه و در صورتی که قسمت اعشاری به یک نزدیک تر بود، حاصل قسمت صحیح ورودی به اضافه یک باشه. حالا اگه نتونیم بگیم قسمت اعشاری به صفر نزدیک تره یا به یک، یعنی اعشار عدد ما دقیقا برابر 0.5 باشه، دیگه باید خودمون یه تصمیمی بگیریم. مثلا توی کد اول آقا محمود تصمیم بر این بوده که در این شرایط همیشه به بالا گرد بشه. ولی رایج اینه که در این شرایط نیمی از ورودی ها به بالا و نیم دیگه به پائین گرد بشن، مثلا فرد یا ذوج بودن قسمت صحیح ملاک خوبی برای این تصمیم گیریه. اینو یه نگاه بکنید، اعداد مختلف با اعشار 0.5 وارد کنید:
Caption := IntToStr(Round(StrToFloatDef(Edit1.Text, 0.0)))
حالا ما یه سری مفاهیم دیگه داریم تحت عنوان "گرد به بالا" و "گرد به پائین" که دیگه مشخصن و اون دنگ و فنگ بالا رو ندارن. معادل اونها در دلفی هم Ceil و Floor هستند که توی Math تعریف شدن.
حالا در مورد مسئله موجود ما می خوایم بازه یا گام گرد کردن رو از 0 .. 1 به 0 .. Step تغییر بدیم. اگه فرض بر حفظ مفاهیم بالا باشه ساده ترین جواب اینه:
function RoundToRange(Value, Range: Integer): Integer;
begin
if Range = 0 then
raise Exception.Create('Range can not be zero!');
Result := Round(Value / Range) * Range;
end;
که حالا با توجه به کاربرد به جای Round میشه از Ceil یا Floor هم به همین شکل استفاده کرد.
تابعی هم که آقا محمود قرار دادن تصور می کنم برهمین اساس پیاده شده و من فکر می کنم کد اول ایشون صحیح و کد دوم دقیقا اشتباه هست.
وقتی از تابعی که زحمت کشیدید و نوشتیم استفاده میکنیم اگر مقدار گام رو 25 قرار بدیم
عدد 1237 چرا میشه 1225 در صورتی که قرار بود بشه 1250.
ببینید بر اساس اون تعریفی که گفتم جواب صحیح با این وردودی ها دقیقا 1225ه. چون 12 به صفر نزدیکتره تا 25. حالا من نمی دونم با تبدیل ملاک تصمیم گیری از 12.5 به 12 دقیقا چیکار کردیم؟ نتیجه میشه:
کد شما اعدادی که به 12 ختم میشه رو نمیتونه گرد کنه.
حالا دوباره برگردیم سر صورت مسئله:
گام رو باید روی چند بزارم که اگه وارد کردم 124 بشود 100
اگه وارد کردم 125 بشه 125
اگه وارد کردم 126 بشود 150.
ببینید این چیزی که شما می خواید تابع گرد کردن نیست، یه تابع دیگست. این تابع دقیقا توی اون شرایطی که گفتم باید خودمون تصمیم بگیریم اینطور تصمیم میگیره که در شرایط مرزی اصلا بیخیال گرد کردن بشه. که مثلا اینجوری میشه:
function MyRoundToRange(Value, Range: Integer): Integer;
begin
if Range = 0 then
raise Exception.Create('Range can not be zero!');
if (Value mod Range) = Range / 2 then
Result := Value
else
Result := Round(Value / Range) * Range;
end;
البته بازه با توجه به شرایط مسئله شما 50 میشه.
حالا برای رسیدن به کارایی بیشتر میشه یه بخشی از این کارهارو به جای تقسیم های اعشاری با تقسیم های صحیح پیاده کرد، که دیگه همون div و mod و غیرست که اگه مورد استفادتون پردازش سنگینی نیست لزومی نداره.