![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Мат, округлние [D4]
pasha_golub © (2004-10-28 14:59:00)Люди, добрые подскажите параметр для FPU 8087, которое нужно кинуть в Set8087CW для того, чтобы округление было не банковским, а математическим. Спасибо.
Verg (2004-10-28 23:29:00) [21]
> [30] sniknik © (28.10.04 23:25)
а на 2.001 что получим?
sniknik (2004-10-28 23:25:00) [22]GuAV © (28.10.04 22:22) [27]
procedure TForm1.Button2Click(Sender: TObject);
var
S, S1: extended;
begin
S:= 1.5;
S1:= 2.5;
Set8087CW($1B72);
Edit1.Text:=IntToStr(Round(S)); // получим 2
Edit2.Text:=IntToStr(Round(S1)); // получим 3
end;
надо разделять, что считается на этапе компиляции а что в рантайме.
Verg (2004-10-28 22:40:00) [23]
> [23] Defunct © (28.10.04 21:32)
Вот и получается: как не крути, а ОКРУГЛЕНИЕ(-1.5) = -2
Verg (2004-10-28 22:25:00) [24]В этом месте полезно напомнить правила округления десятичных дробей до определенного разряда. Округление состоит в отбрасывании, точнее в замене нулями, всех цифр, стоящих правее цифры данного разряда. Если при этом первой из отбрасываемых цифр является 0, 1, 2, 3 или 4 , последняя остающаяся цифра не меняется; если же первой из отбрасываемых цифр окажется 5, 6, 7, 8 или 9 , последняя остающаяся цифраувеличивается на 1, а если этой цифрой была 9, то увеличение на 1 произойдет в предшествующем ей разряде, а сама она заменится на 0. Старинное "правило четной цифры": когда отбрасывается только цифра 5, последняя остающаяся цифра не меняется, если она четная, и увеличивается на 1, если она нечетная.
http://www.sgu.ru/ie/mehmat/matin/
http://www.ibase.ru/devinfo/round.htm
GuAV (2004-10-28 22:22:00) [25]sniknik © (28.10.04 22:12) [25]
procedure TForm1.Button2Click(Sender: TObject);
begin
Set8087CW($1B72);
Edit1.Text:=IntToStr(Round(1.5)); // получим 2
Edit1.Text:=IntToStr(Round(2.5)); // получим 2
end;
и вообще кроме 10 и 11 битов не следует ничего торгать, особенно exception mask.
GuAV (2004-10-28 22:15:00) [26]Я уже доказал.
Ещё раз
Вот допустим ххх.5 и .5 в последнем знаке, прибавление 0.1 не изменит число, и если оно нечётноё, оно округлится в меньшую сторону ка и просто Round.
При том, что может быть в том же месте и ххх.49, прибавив 0.1 получим ххх.59
Defunct © (28.10.04 22:03) [24]
Понимаю, что вам немного неприятно, что вы не заметили такого простого способа решения.
Это могло бы быть применимо к Verg © (28.10.04 17:52) [11], но не к Defunct © (28.10.04 18:10) [12]. А вообще никакой неприязни не испытываю.
sniknik (2004-10-28 22:12:00) [27]
procedure TForm1.Button1Click(Sender: TObject);
var
Table: array[word] of double;
i: integer;
S, S1, S2: extended;
begin
randomize;
S := 0;
for i := low(Table) to high(Table) do begin
Table[i] := Random * 10;
S := S + Table[i]; // это будет точная сумма
end;
Set8087CW($1372); // включаем "бухгалтерское" округление
S1 := 0;
for i := low(Table) to high(Table) do S1 := S1 + Round(Table[i]);
Set8087CW( $1B72); // включаем "школьное" округление
S2 := 0;
for i := low(Table) to high(Table) do S2 := S2 + Round(Table[i]);
Edit1.Text:= FloatToStr(S);
Edit2.Text:= FloatToStr(S1);
Edit3.Text:= FloatToStr(S2);
end;
Defunct (2004-10-28 22:03:00) [28]> но правильное (в отличии от Вашего).
Чтобы так говорить надо доказать, что мое решение - неправильное.
Вперед, жду доказательства.
Понимаю, что вам немного неприятно, что вы не заметили такого простого способа решения.
Defunct (2004-10-28 21:32:00) [29]> -1.5 - это сколько должно получится после округления?
-1
GuAV (2004-10-28 21:14:00) [30]Defunct © (28.10.04 19:56) [20]
В случае бесконечно больших чисел округление не имеет значения вообще
Мы не говорим о бесконечно больших числах, а о просто больших. Я говорю о том что могут существовать и болшие числа, где Вашим способом меньше 0.01 не получится и малые, где 0.0001 внесёт искажение.
Defunct © (28.10.04 19:56) [20]
Это "красивое" решение как минимум в 2 раза медленнее моего и даже медленнее вашего решения
Мы не меряемся скоростью мы решаем задачу. Оно красивое, несмотря на то что возможно и самое медленное, т.к. простое (в отличии от моего) но правильное (в отличии от Вашего).
Verg (2004-10-28 20:45:00) [31]
> Defunct © (28.10.04 19:56)
Ты лучше вот что скажи:
-1.5 - это сколько должно получится после округления?
Defunct (2004-10-28 19:56:00) [32]Уж кто как не вы, зная asm, должны понимать что:
> Не говоря уже о красивомрешении Verg
Это "красивое" решение как минимум в 2 раза медленнее моего и даже медленнее вашего решения (которое не блещет).
> Согласитесь Ваша идея даже хуже моей.
Не соглашусь, потому что мы живем в реальном мире и мат расчеты имеют дело с реальными числами а не с бесконечностью. И для реальных чисел метод [12] самый быстрый и его нельзя упрекнуть в недостатке точности.
В случае бесконечно больших чисел округление не имеет значения вообще. Какая разница будет там 1 или 2 в последнем разряде если число состоит из 50-ти цифр?
"Программист! ДЕЛАЙ ВСЕ ПРОЩЕ".
В данном конкретном случае, когда у нас точность до "целого" подойдет и:
Round( Число + 0.1)
GuAV (2004-10-28 19:33:00) [33]Defunct © (28.10.04 19:22) [18]
Вы идею вообще поняли
Идею понял. Но она не очень хорошая. Допустим у нас есть числа где точность 10(-6) и есть те у которых не может быть такой точности (из-за ограничения мантиссы extended). Для них уже нужны разные E.
Согласитесь Ваша идея даже хуже моей. Не говоря уже о красивом решении Verg © (28.10.04 17:52) [11].
Defunct (2004-10-28 19:22:00) [34]> Тогда для каждого числа надо подбирать своё E.
E подбирается под точность, а не для каждого числа.
Для точности 10(-6), порядок E должен быть меньше либо равен 10(-7).
> Дело в том что для числа xxxxxx.5 этот самый .5 может быть в
> последнем разряде, т.е. прибавление 0.1 не изменит того числа.
> Искать можество таких чисел не буду, но оно есть.
А и не нужно ничего изменять.
должно быть:
Round(xxxxx.5 + 0.01)
или
Round(xxxxx.5 + 0.001)
Главное, что порядок единицы ниже.
Вы идею вообще поняли или просто видите какие-то ошибки, понятные только вам?
GuAV (2004-10-28 19:14:00) [35]Defunct © (28.10.04 18:50) [15]
Тогда для каждого числа надо подбирать своё E.
Дело в том что для числа xxxxxx.5 этот самый .5 может быть в последнем разряде, т.е. прибавление 0.1 не изменит того числа. Искать можество таких чисел не буду, но оно есть.
Defunct © (28.10.04 18:56) [16]
но поверьте наслово Mat округление делаем приведенным в [12] методом еще с 8087.
На здоровье !
Но всё же пока я вижу ошибку в Вашем методе, я не стал бы его использовать/рекомендовать его, и автор векти вероятно тоже.
Defunct (2004-10-28 18:56:00) [36]GuAV © (28.10.04 18:22) [13]
PS: в общем у меня сегодня жутко плохое настроение чтобы привести нормальные аргументы, да и тема заезженная и неинтересная, но поверьте наслово Mat округление делаем приведенным в [12] методом еще с 8087.
Defunct (2004-10-28 18:50:00) [37]GuAV © (28.10.04 18:22) [13]
Вам что надо все разжевать?
Порядок единички E должен быть меньше порядка самой младшей цифры после запятой. Если мы работаем с точностью до 10(-6) = 0.000001 тогда E дожно быть E=10(-7) = 0.0000001.
ПРИЧЕМ ВСЕГДА.
GuAV (2004-10-28 18:34:00) [38]Вот мой вариант. Можно конечно избавится от try...finally убедившись что исключений не будет (замаскировать все FPU исключения)
function MathRound(const X: Extended): Extended;
var OldRMode: TFPURoundingMode;
begin
if X > 0 then
begin
OldRMode := SetRoundMode(rmDown);
try
Result:=Round(X + 0.5);
finally
SetRoundMode(OldRMode);
end;
end
else
begin
OldRMode := SetRoundMode(rmUp);
try
Result:=Round(X - 0.5);
finally
SetRoundMode(OldRMode);
end;
end
end;
procedure TForm1.Button1Click(Sender: TObject);
var ValueToRound: Extended;
begin
ValueToRound := 0;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := 1.499999999999;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := 1.5;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := 2.499999999999;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := 2.5;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := -1.499999999999;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := -1.5;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := -2.499999999999;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
ValueToRound := -2.5;
memo1.Lines.Add(FloatToStr(MathRound(ValueToRound)));
end;
GuAV (2004-10-28 18:22:00) [39]Defunct © (28.10.04 18:10) [12]
Не всегда.
procedure TForm1.FormCreate(Sender: TObject);
Const E = 0.000000000001;
var ValueToRound: Extended;
begin
ValueToRound := 1.499999999999;
memo1.Lines.Add(FloatToStr(Round(ValueToRound)));
memo1.Lines.Add(FloatToStr(Round(ValueToRound+E)));
end;
Defunct (2004-10-28 18:10:00) [40]pasha_golub © (28.10.04 15:22) [5]
Уже честно сказать надоело попугайничать, но так уж и быть специально для вас:
Const E = 0.000000000001;
Round( Число + E );
Будет вам мат. округление.