Форум: "Основная";
Текущий архив: 2004.11.21;
Скачать: [xml.tar.bz2];
ВнизМат, округлние Найти похожие ветки
← →
pasha_golub © (2004-10-28 14:59) [0]Люди, добрые подскажите параметр для FPU 8087, которое нужно кинуть в Set8087CW для того, чтобы округление было не банковским, а математическим. Спасибо.
← →
GuAV © (2004-10-28 15:07) [1]pasha_golub © (28.10.04 14:59)
SetRoundMode
rmNearest Rounds to the closest value.
rmDown Rounds toward negative infinity.
rmUp Rounds toward positive infinity.
rmTruncate Truncates the value, rounding positive numbers down and negative numbers up.
AFAIK, это всё что есть - математического нет.
← →
GuAV © (2004-10-28 15:14) [2]
15 8 7 0
+-----------------------++-----------------------+
¦ ¦ ¦ ¦ ¦ ¦ ¦¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦IC¦ RC ¦ PC ¦¦IE¦ ¦PM¦UM¦OM¦ZM¦DM¦IM¦-- Control word
¦ ¦ ¦ ¦ ¦ ¦ ¦¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
+-----------------------++-----------------------+
IC--Infinity Control
0 = Projective (default on 8087 and 80287)
1 = Affine
On 80387 and 80486, affine closure (distinguishing between positive
and negative infinity) is used regardless of setting.
RC--Rounding Control
00 = Round to nearest or even (default)
01 = Round down toward -infinity
10 = Round up toward +infinity
11 = Chop by truncating toward 0
PC--Precision control
00 = 24-bit mantissa (single precision)
10 = 53-bit mantissa (double precision)
11 = 64-bit mantissa (extended precision)
IE - Interrupt Enable Mask
Only on 8087; undefined on 80287, 80387, and 80486.
Exception Masks
PM = 1 to mask precision
UM = 1 to mask underflow
OM = 1 to mask overflow
ZM = 1 to mask zero divide
DM = 1 to mask denormalized operand
IM = 1 to mask invalid operation
← →
pasha_golub © (2004-10-28 15:14) [3]Есть, есть... Блин, мать его за ногу.
← →
pasha_golub © (2004-10-28 15:17) [4]Вот те раз... Вот те два... Спасибо.
← →
pasha_golub © (2004-10-28 15:22) [5]Лана, а как же его тогда реализовать? Блин.
← →
Jeer © (2004-10-28 15:28) [6]function RoundUp(Value: Extended): Int64;
const
RoundUpCW = $1B32;
var
OldCW: Word;
begin
OldCW := Default8087CW;
try
Set8087CW(RoundUpCW);
result := Round(Value);
finally
Set8087CW(OldCW);
end;
end;
end;
← →
GuAV © (2004-10-28 16:06) [7]Да, выставить округление вверх, а перед округлением отнять .5
Только рекомендую не кодом Jeer ©, а через SetRoundMode, чтоб лишние биты не менять.
← →
GuAV © (2004-10-28 16:09) [8]GuAV © (28.10.04 16:06) [7]
То есть наоборот, вниз и добавить .5 и в коде Jeer © ничего плохого...
← →
GuAV © (2004-10-28 16:20) [9]Точнее ещё сложнее :( для положительных округление вверх, а перед округлением отнять .5 для отрицательных добавить .5 и вниз.
Хотя не уверен что есть мат. округление для отр чисел.
← →
pasha_golub © (2004-10-28 16:50) [10]Спасибо, ребят.
← →
Verg © (2004-10-28 17:52) [11]Может такая подойдет:
function RoundUp(X: Extended): Extended;
begin
Result := Trunc(X) + Trunc (Frac(X) * 2);
end;
← →
Defunct © (2004-10-28 18:10) [12]pasha_golub © (28.10.04 15:22) [5]
Уже честно сказать надоело попугайничать, но так уж и быть специально для вас:Const E = 0.000000000001;
Round( Число + E );
Будет вам мат. округление.
← →
GuAV © (2004-10-28 18:22) [13]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;
← →
GuAV © (2004-10-28 18:34) [14]Вот мой вариант. Можно конечно избавится от 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;
← →
Defunct © (2004-10-28 18:50) [15]GuAV © (28.10.04 18:22) [13]
Вам что надо все разжевать?
Порядок единички E должен быть меньше порядка самой младшей цифры после запятой. Если мы работаем с точностью до 10(-6) = 0.000001 тогда E дожно быть E=10(-7) = 0.0000001.
ПРИЧЕМ ВСЕГДА.
← →
Defunct © (2004-10-28 18:56) [16]GuAV © (28.10.04 18:22) [13]
PS: в общем у меня сегодня жутко плохое настроение чтобы привести нормальные аргументы, да и тема заезженная и неинтересная, но поверьте наслово Mat округление делаем приведенным в [12] методом еще с 8087.
← →
GuAV © (2004-10-28 19:14) [17]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 19:22) [18]> Тогда для каждого числа надо подбирать своё 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:33) [19]Defunct © (28.10.04 19:22) [18]
Вы идею вообще поняли
Идею понял. Но она не очень хорошая. Допустим у нас есть числа где точность 10(-6) и есть те у которых не может быть такой точности (из-за ограничения мантиссы extended). Для них уже нужны разные E.
Согласитесь Ваша идея даже хуже моей. Не говоря уже о красивом решении Verg © (28.10.04 17:52) [11].
← →
Defunct © (2004-10-28 19:56) [20]Уж кто как не вы, зная asm, должны понимать что:
> Не говоря уже о красивом решении Verg
Это "красивое" решение как минимум в 2 раза медленнее моего и даже медленнее вашего решения (которое не блещет).
> Согласитесь Ваша идея даже хуже моей.
Не соглашусь, потому что мы живем в реальном мире и мат расчеты имеют дело с реальными числами а не с бесконечностью. И для реальных чисел метод [12] самый быстрый и его нельзя упрекнуть в недостатке точности.
В случае бесконечно больших чисел округление не имеет значения вообще. Какая разница будет там 1 или 2 в последнем разряде если число состоит из 50-ти цифр?
"Программист! ДЕЛАЙ ВСЕ ПРОЩЕ".
В данном конкретном случае, когда у нас точность до "целого" подойдет и:Round( Число + 0.1)
← →
Verg © (2004-10-28 20:45) [21]
> Defunct © (28.10.04 19:56)
Ты лучше вот что скажи:
-1.5 - это сколько должно получится после округления?
← →
GuAV © (2004-10-28 21:14) [22]Defunct © (28.10.04 19:56) [20]
В случае бесконечно больших чисел округление не имеет значения вообще
Мы не говорим о бесконечно больших числах, а о просто больших. Я говорю о том что могут существовать и болшие числа, где Вашим способом меньше 0.01 не получится и малые, где 0.0001 внесёт искажение.
Defunct © (28.10.04 19:56) [20]
Это "красивое" решение как минимум в 2 раза медленнее моего и даже медленнее вашего решения
Мы не меряемся скоростью мы решаем задачу. Оно красивое, несмотря на то что возможно и самое медленное, т.к. простое (в отличии от моего) но правильное (в отличии от Вашего).
← →
Defunct © (2004-10-28 21:32) [23]> -1.5 - это сколько должно получится после округления?
-1
← →
Defunct © (2004-10-28 22:03) [24]> но правильное (в отличии от Вашего).
Чтобы так говорить надо доказать, что мое решение - неправильное.
Вперед, жду доказательства.
Понимаю, что вам немного неприятно, что вы не заметили такого простого способа решения.
← →
sniknik © (2004-10-28 22:12) [25]
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;
← →
GuAV © (2004-10-28 22:15) [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]. А вообще никакой неприязни не испытываю.
← →
GuAV © (2004-10-28 22:22) [27]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.
← →
Verg © (2004-10-28 22:25) [28]В этом месте полезно напомнить правила округления десятичных дробей до определенного разряда. Округление состоит в отбрасывании, точнее в замене нулями, всех цифр, стоящих правее цифры данного разряда. Если при этом первой из отбрасываемых цифр является 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
← →
Verg © (2004-10-28 22:40) [29]
> [23] Defunct © (28.10.04 21:32)
Вот и получается: как не крути, а ОКРУГЛЕНИЕ(-1.5) = -2
← →
sniknik © (2004-10-28 23:25) [30]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 23:29) [31]
> [30] sniknik © (28.10.04 23:25)
а на 2.001 что получим?
← →
Defunct © (2004-10-28 23:33) [32]GuAV © (28.10.04 22:15) [26]
> Я уже доказал.
Не не доказал.
Док-во - конкретный пример где такое округление даст сбой, а ваш способ и ф-ция Verg округлит правильно.
> Вот допустим ххх.5 и .5 в последнем знаке, прибавление 0.1 не
> изменит число, и если оно нечётноё, оно округлится в меньшую
> сторону ка и просто Round.
Давайте без допустим. Допустим в мантиссу не влазит дробная часть вообще. В общем это чушь а не доказательство.
> Вот и получается: как не крути, а ОКРУГЛЕНИЕ(-1.5) = -2
В математике все же округление идет к большему целому.
Просто и со вкусом:Const E = 0.01;
procedure TForm1.Button8Click(Sender: TObject);
begin
ShowMessage( Format("]-2.5[ = %D",[ Round(-2.5 + E) ] ));
ShowMessage( Format("]-1.5[ = %D",[ Round(-1.5 + E) ] ));
ShowMessage( Format("]1.5[ = %D",[ Round(1.5 + E) ] ));
ShowMessage( Format("]2.5[ = %D",[ Round(2.5 + E) ] ));
end;
← →
sniknik © (2004-10-28 23:54) [33]> а на 2.001 что получим?
обана, а мужики то не в курсе. ;о)
сорри, действительно ерунда получилась.
← →
Defunct © (2004-10-28 23:56) [34]> Verg
> http://www.sgu.ru/ie/mehmat/matin/
Извини, но я там не нашел ни слова про округление..
То, что по второй ссылке -1.5 = -2 я поверю наслово.
← →
False_Delirium © (2004-10-29 00:04) [35]Defunct © (28.10.04 23:56) [34]
Математическое округление -1.5 даёт -1
и кто говорит обратное - идёт в туман :))
← →
Verg © (2004-10-29 00:04) [36]
> [34] Defunct © (28.10.04 23:56)
http://www.sgu.ru/ie/mehmat/matin/R1-4.htm
← →
Defunct © (2004-10-29 00:31) [37]Verg © (29.10.04 00:04) [36]
ага нашел.
Округление состоит в отбрасывании, точнее в замене нулями, всех цифр, стоящих правее цифры данного разряда. Если при этом первой из отбрасываемых цифр является 0, 1, 2, 3 или 4 , последняя остающаяся цифра не меняется; если же первой из отбрасываемых цифр окажется 5, 6, 7, 8 или 9 , последняя остающаяся цифра увеличивается на 1
Давайте применим это правило к числам около нуля.
-0.6 = -1
-0.5 = -1
-0.4 = 0
-0.3 = 0
-0.2 = 0
-0.1 = 0
0 = 0
0.1 = 0
0.2 = 0
0.3 = 0
0.4 = 0
0.5 = 1
Получается почему-то 9 нулей хотя должно быть 10. ведь целое складывается из десяти десятых, а не из девяти.
Выводы:
1. 0 - не целое число (но это не так).
2. метод округления указан не точно. И действительно в приведенной статье нет правила округления отрицательных чисел.
← →
GuAV © (2004-10-29 00:35) [38]Defunct © (28.10.04 23:33) [32]
Док-во - конкретный пример где такое округление даст сбойprocedure TForm1.FormCreate(Sender: TObject);
var X, X1, Z, Z1: Extended; S: string; OldMode: TFPURoundingMode;
begin
Z:=212432313499856792.5;
X:=212432313.4998567925;
Memo1.Lines.Add("Defunct");
Z1:=Round(Z + 0.001);
X1:=Round(X + 0.001);
Memo1.Lines.Add(FloatToStrF(Z1, ffFixed, 22, 2));
Memo1.Lines.Add(FloatToStrF(X1, ffFixed, 22, 2));
Memo1.Lines.Add("Verg");
Z1:=Trunc(Z) + Trunc (Frac(Z) * 2);
X1:=Trunc(X) + Trunc (Frac(X) * 2);
Memo1.Lines.Add(FloatToStrF(Z1, ffFixed, 22, 2));
Memo1.Lines.Add(FloatToStrF(X1, ffFixed, 22, 2));
Memo1.Lines.Add("GuAV");
OldMode := SetRoundMode(rmDown);
try
Z1:=Round(Z + 0.5);
X1:=Round(X + 0.5);
finally
SetRoundMode(OldMode);
end;
Memo1.Lines.Add(FloatToStrF(Z1, ffFixed, 22, 2));
Memo1.Lines.Add(FloatToStrF(X1, ffFixed, 22, 2));
end;
← →
Verg © (2004-10-29 00:41) [39]Можете еще с excel-ом поиграться. Тот использует математическое округление без правила "четной цифры". Функция =ОКРУГЛ().
А
>В математике все же округление идет к большему целому.
> Const E = 0.000000000001;
>
> Round( Число + E );
Образует какое-то "альтернативное" понятие математического округления :))
← →
Defunct © (2004-10-29 00:53) [40]GuAV © (29.10.04 00:35) [38]
Извините, но вы показываете свое неумение слушать.
X:=212432313.4998567925;
Конкретно в этом числе порядок наименьшего значащей цифры (5) = 10(-10). Соответственно E = 10(-11).
Все упирается в точность.
Опять же
Z:=212432313499856792.5;
при E=0.01 округлит верно.
для полноты картины надо бы добавить еще и
Y = 3212432313499856792.5;
В общем если вы пытаетесь показать ограниченность мантиссы, так с этим никто не спорит.
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2004.11.21;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.036 c