Закръгляването в Delphi

11 отговора 729 прегледа
#1

Закръгляването в Delphi

Мисля, че доста отдавна го дискутирахме това, но наскоро закръгляването в Delphi отново събуди моето любопитство.

Става въпрос за стандартната функция Round. И по-точно, че Round(1.5) = 2 и Round(2.5) = 2 също! Четните и нечетните числа се закръгляват по различен начин. Не е проблем това да се "поправи" с друга функция, но в момента не мога да стопля защо е реализирано така. Идеи?

Mixy
Отговор
в отговор на Mixy #2

Re: Закръгляването в Delphi

The rounding uses Bankers rules, where an exact half value causes a rounding to an even number

Отговор
в отговор на Mixy #3

Re: Закръгляването в Delphi

И за да не се мъчиш:
function RoundCorrect(R: Real): LongInt;
begin
Result:= Trunc(R);
if Frac(R) >= 0.5 then
Result:= Result + 1;
end;


Отговор
в отговор на ИвKo #4

Re: Закръгляването в Delphi

Мерси, че ми спести мъките, относно "правилния" алгоритъм.

Но по-интересни са ми именно тези банкови правила и какво ги налага?

Mixy
Отговор
в отговор на Mixy #5

Re: Закръгляването в Delphi

Някъде на delphikingdom имаше статия по въпроса, но общо взето логиката е следната:
Закръгляването при *5 до по горното число е несиметрично, което при сумиране на много числа дава систематично отклонение нагоре, което не е верно при симетричното закръгляване до по-близкото четно.

Напр. нека да разгледаме числата:

0,1 0 0
0,2 0 0
0,3 0 0
0,4 0 0
0,5 1 0
0,6 1 1
0,7 1 1
0,8 1 1
0,9 1 1
1,0 1 1
1,1 1 1
1,2 1 1
1,3 1 1
1,4 1 1
1,5 2 2
1,6 2 2
1,7 2 2
1,8 2 2
1,9 2 2
2,0 2 2
==========
21 22 21

Отговор
в отговор на Mixy #6

Re: Закръгляването в Delphi

NDeu е дал добър отговор (не го знаех). Мерси NDeu.
Наистина и аз съм си задавал този въпрос. Спрях да си го задавам когато почнах да ползвам:

SimpleRoundTo

Дефинирана е в unita Math в Delphi 7.

Отговор
в отговор на Mixy #7

Re: Закръгляването в Delphi

За този род сметки използвам Currency вместо double с ф-ята FormatFloat().
Пиша на Буилдера, но се надявам, че и в Делфито има съответните аналози.

Отговор
в отговор на ИвKo #8

Re: Закръгляването в Delphi

ИвКо, твоята функция не работи правилно за отрицателни числа.

Отговор
в отговор на Mandor #9

Re: Закръгляването в Delphi

Мдааааааа.... Така е като пиша направо в интернет екплорера...
Извинявайте... друг път ще трябва да си отварям Делфито

Отговор
в отговор на Mixy #10

Re: Закръгляването в Delphi

OK, мерси на всички!

Това за натрупването при сумиране на закръглени числа не ми беше хрумвало.



Mixy
Отговор
в отговор на Veso #11

Re: Закръгляването в Delphi

мда хубава идейка, но ако от базата данни ти идва стойност NULL полето я прави 0.00 лв примерно (тоест на поле без стойност слага стойност - макар и нула)
или бъркам?

Отговор
в отговор на Vermax #12

Re: Закръгляването в Delphi

Винаги преди присвояването към съответният тип можеш да провериш предварително с IsNull() това условие.
Напоследък и аз отново почоплих този въпрос и ето до какви резултати стигнах със закръглянията на double в стринг:
- FormatFloat() - банкерско закръгляне т.е. грешно
- sprintf() на класа AnsiSting -банкерско закръгляне т.е. грешно
- sprintf() от стандартната С библиотека - банкерско закръгляне т.е. грешно
- snprintf() от стандартната С библиотека - банкерско закръгляне т.е. грешно
- wsprintf() - липсва %f типов спецификатор. Компилира се, но гърми при
изпълнение с ВСВ-то
- RoundTo() - вярно конвертиране
- SimpleRoundTo() - вярно конвертиране
- gcvt(),ecvt() и тем подобни отпадат предварително като морално остарели

А ето и самият пример
На празна форма се добавя RichEdit и бутон. При натискането на бутона се изпълнява:
int i5,i4;
double d;
String s;
//char szBuf[10]; // за C style sprintf()

i4 = i5 = 0;
RichEdit1->Lines->Clear();

for (int i = 0; i < 1000; i++)
{ d = double(i + 0.45);
s.sprintf("%5.1f",d); // смени sprintf() с другите ф-ии
RichEdit1->Lines->Add(s);
if (s[s.Length()] == '5')
i5++;
else
i4++;
}
RichEdit1->Lines->Add("Round to 4 = " + String(i4));
RichEdit1->Lines->Add("Round to 5 = " + String(i5));

1-те 10 реда в RichEdit-a
0.5
1.4
2.5
3.5
4.5
5.5
6.5
7.5
8.4
9.4

Интересното е ,че с Currency вместо double горният пример работи нормално.
sprintf() на стандартната С библиотека оттпада и поради друго съображение - възможно препълване на буфера. snsprintf() е по добрият вариант в това отношение.

Отговор