Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  10:57[Войти] | [Зарегистрироваться]

Обсуждение материала
Загадки округления. Тестирование функций округления различных языков.
Полный текст материала


Другие публикации автора: Алексей Михайличенко

Цитата или краткий комментарий:

«... Чтобы поставить всех тестируемых в равные условия, вся работа велась через текстовые файлы. От программы требовалось считать файл с исходными данными, округлить числа, и построчно записать результат в другой файл. Таким образом, особенности внутреннего представления вещественных чисел на данном языке и соответствующих преобразований оставались на совести самой тестируемой программы. ...»


Важно:
  • Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
  • Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
  • При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
  • Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.



Добавить свое мнение.

Результаты голосования
Оценка содержания

  Содержит полезные и(или) интересные сведения
[1]7100%
 
  Ничего особенно нового и интересного
[2]00%
 
  Написано неверно (обязательно укажите почему)
[3]00%
 
Всего проголосовали: 7

Оценка стиля изложения

  Все понятно, материал читается легко
[1]685.7%
 
  Есть неясности в изложении
[2]114.3%
 
  Непонятно написано, трудно читается
[3]00%
 
Всего проголосовали: 7




Смотрите также материалы по темам:
[Математические функции]

Комментарии жителей
Отслеживать это обсуждение

Всего сообщений: 23

02-11-2009 03:55
Кстати, да, хоть я и использую переменную типа Extended, но я ей присваиваю результат деления чисел, взятых из полей объекта TQuery. Вполне возможно, что там тип Double и хранится он вот так. Но это уже оффтоп.

Простите, что сбаламутил. Джоново округление тоже верное в пределах "правильных цифр" числа :-)


30-10-2009 08:13
Что я делаю не так?

Не тот тип используете :)

В числе 4782.52499999999983 всего 16 правильных цифр, т.е. это не Extended, а Double.


30-10-2009 07:59
сообщение от автора материала
Пробую.

procedure TForm1.Button1Click(Sender: TObject);
var x, y, z, z2: extended;
begin
  x := 9565.05;
  y := 2;
  z := x / y;
  Edit1.Text := FloatToStr(z);  // 4782,525
  z2 := DecimalRoundExt(z, 2, drHalfUp);
  Edit2.Text := FloatToStr(z2); // 4782,53
end;



Что я делаю не так?


30-10-2009 06:52
2 Fisher:
Математически - это результат деления двух чисел, и он равен точно 4782.525, а хранится в переменной как 4782.52499999999983.
Проблема ведь при округлениях только с этим. Округлить правильно ровно 4782.525 почти все могут.


28-10-2009 06:29
сообщение от автора материала
to VovaVV: Не совсем понял про это ext: каким оно должно быть, и когда + а когда -? Просьба оформить этот код в виде готовой функции для округления до двух знаков после запятой.

И про 4782.52499999999983 - это число по всем правилам должно округляться вниз, до 4782.52. При чем же здесь John Herbster?


28-10-2009 04:17
Я для себя давно написал функцию, которая пока меня не подводила:

  Result := Trunc(ValExt) + Trunc(Frac(ValExt)*2);
где ValExt = Value * Scale +/- ext;
где Value - округляемое значение
    Scale - множитель, зависящий от необходимой точности округления (f.e. точность: 2 знака после запятой, множитель = 100),
    ext   - прибавка, гарантированно меняющая разряд в "проблемных" числах (можно ставить вплоть до = 0.09),
ext прибавлять или вычитать - зависит от знака Value

Просьба автору материала потестировать её тоже, если есть желание.


28-10-2009 03:48
Правда, придется избавляться от "усердности" округления (1.42499 должно быть все-таки 1.42)


28-10-2009 03:42
Исходники качал вчера с Borland DevNetwork (файл DecimalRounding_JH1.pas 14046 байт от 15.06.05)
Пробовал ставить Sеt8087CW, как рекомендовали ниже, результат одинаковый.
Да оно и понятно: число 4782.525 приходит в переменной типа extended, хранится оно (у меня) на самом деле как 4782.52499999999983, а перед округлением
    drHalfUp:    {Round to nearest or away from zero.}
      i64 := round((abs(ScaledVal) + ScaledErr));
Джон прибавляет мизерное число ScaledErr, которое третью после запятой 4ку не делает 5кой и все выкрутасы тут бесполезны.
Функция округления будет работать всегда правильно, если ScaledErr будет не мизером типа 10e-19, а 0.1e-"Точность округления", чтобы гарантированно "переворачивать" следующий разряд.


27-10-2009 12:43
Не все так радужно и у DecimalRoundExt(n, 2, drHalfUp) by John Herbster:
Проверьте какая у вас версия функции, а то он её обновляет по мере нахождения багов.
 DRON


27-10-2009 11:50
>>> Не все так радужно и у DecimalRoundExt
А вы автору это отправляли?


27-10-2009 11:31
сообщение от автора материала
Хм. А у меня получается 4782,53. Проверял инспектором в точке останова. D7.



27-10-2009 08:38
Не все так радужно и у DecimalRoundExt(n, 2, drHalfUp) by John Herbster:
DecimalRoundExt(4782.525, 2, drHalfUp)=4782.52, а должно быть 4782.53 (мат.округление)


25-06-2009 12:21
На алгоритм Джона Хербстера влияет значение FPU Control Word , вернее свойство Precision (т.е. длина мантиссы  - 24, 52, 64бита)- надо чтобы точность соответствовала той функции округления, которую вы используете - короче ставьте 64 бита. Надо перед вычислениями каждый раз выставлять контрольное слово Sеt8087CW(например $1332) т.к. сопроцессор один на всю систему и любая свистелка может его сбить.


11-07-2007 05:58
Небольшоепримечание для Delphi:
- ошибка округления зависит от типа данных:    
  StrToFloat(FloatToStrF(...
  Trunc(b * 100 + 0.5) / 100
  работают нормально на данных типа Currency,
  для данных типа Double дают ошибочные значения
  


06-05-2006 04:11
сообщение от автора материала
В строке 2 в графе 4 стоит "Б(?)", это означает что в этой строке мы пытаемся сравнивать функцию sprintf с алгоритмом банковского округления. А это и есть округление до четного: 2.5 -> 2.0, 3.5 -> 4.0

Так как в perldoc не указано, какой именно алгоритм реализуется sprintf (и даже явно рекомендуется использовать другие функции, если это важно), то мы сравниваем sprintf и с арифметическим, и с банковским округлением - чтобы убедиться, что она не является ни тем ни другим.

perlfaq4 - Data Manipulation

Rounding in financial applications can have serious implications, and the rounding method used should be specified precisely. In these cases, it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.

To see why, notice how you'll still have an issue on half-way-point alternation:

    for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}

    0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
    0.8 0.8 0.9 0.9 1.0 1.0

Don't blame Perl. It's the same as in C. IEEE says we have to do this. Perl numbers whose absolute values are integers under 2**31 (on 32 bit machines) will work pretty much like mathematical integers. Other numbers are not guaranteed.



06-05-2006 03:34
Непонял, с каких пор

0.0250 почему должно быть 0.02
0.0650  ---   0.06

встроенная, sprintf("%3.2f") п. 2







26-04-2006 00:33
сообщение от автора материала
Sorry, недочитал. Для Floating типов оно осталось как и раньше на совести ОС-библиотек, но MySQL почему-то убежден, (наверное, в соответствии с IEEE-754, наивный), что большинство библитотек сделают бухгалтерское округление.


26-04-2006 00:25
сообщение от автора материала
Правда, выгрузку делал черех
SELECT * INTO OUTFILE
Может быть, из-за этого получилась такая точность?


Нет, конечно ;-) Просто они это ИСПРАВИЛИ!!!, о чем они кричат, дудят и машут разноцветными флагами на каждом углу своего MySQL 5.0 Reference Manual. Смотрим:

MySQL 5.0 Reference Manual :: 12 Functions and Operators :: 12.4 Numeric Functions :: 12.4.2 Mathematical Functions (http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html)

Нужно также прочесть:

MySQL 5.0 Reference Manual :: 21 Precision Math, (http://dev.mysql.com/doc/refman/5.0/en/precision-math.html)

в частности 21.4 Rounding Behavior (http://dev.mysql.com/doc/refman/5.0/en/precision-math-rounding.html)


Короче говоря, с 5.0.3 они забрали реализацию математики под себя, и очень этим гордятся. Для "точных" типов (INTEGER, DECIMAL, NUMERIC) округление идет арифметическое. Для Floating - бухгалтерское.


Before MySQL 5.0.3, the behavior of ROUND() when the argument is halfway between two integers depends on the C library implementation. Different implementations round to the nearest even number, always up, always down, or always toward zero. If you need one kind of rounding, you should use a well-defined function such as TRUNCATE() or FLOOR()  instead.

As of MySQL 5.0.3, ROUND() uses the precision math library for exact-value arguments when the first argument is a decimal value:

    * For exact-value numbers, ROUND() uses the “round half up” or “round toward nearest” rule: A value with a fractional part of .5 or greater is rounded up to the next integer if positive or down to the next integer if negative. (In other words, it is rounded away from zero.) A value with a fractional part less than .5 is rounded down to the next integer if positive or up to the next integer if negative.

    * For approximate-value numbers, the result depends on the C library. On many systems, this means that ROUND() uses the "round to nearest even" rule: A value with any fractional part is rounded to the nearest even integer.






25-04-2006 17:39
В общем, так... Тестировал на MySQL 5.0.16-nt ОС Windows-2000

Получил арифметическое округление без отклонений для:
ROUND(n1,2)
TRUNCATE(n1+0.005,2)

Правда, выгрузку делал черех
SELECT * INTO OUTFILE

Может быть, из-за этого получилась такая точность?
 Geo


25-04-2006 14:22
сообщение от автора материала
Он создаст один миллион значений, но последним значением будет 104,164,096.9999

По поводу MySQL, я тестировал так:

--run as root
CREATE DATABASE testround;
GRANT ALL PRIVILEGES ON *.* TO fisher IDENTIFIED BY '123';

--run as fisher
use testround;
CREATE TABLE test1 (
n1 DECIMAL(14,4) NOT NULL PRIMARY KEY,
n2  DECIMAL(14,2) NOT NULL
);

Файл DATA.TXT переименовать в TEST1.TXT и загрузить так:

mysqlimport -c N1 --fields-terminated-by=,  --local -d -v -p123 -u fisher testround test1.txt

Затем собственно округляем:

UPDATE test1 SET n2 = ROUND(n1, 2);

Затем выгружаем результат:

mysql -u fisher -p123 < select2.sql > result_mysql.txt

где select2.sql содержит

USE testround;
SELECT * FROM test1;

Убираем заголовок и итог, и сравниваем result_mysql.txt с образцовым файлом, можно просто по fc.

Образцовые файлы формируются так:

Для арифметического округления:

#!perl -w
use strict;

open(FD, "data.txt");
open(FR, ">std_a.txt");
while(<FD>) {
chomp;
my $r = okr_a($_);
print FR "$_ $r\n";
}
close(FR);
close(FD);

sub okr_a {
my $n1 = shift;

my $pos_comma = index($n1, '.');
my $res = substr($n1, 0, $pos_comma + 3);
my $rounded_digit = substr($n1, $pos_comma + 3, 1);
if($rounded_digit >= 5) {
$res = sprintf("%3.2f", ($res + 0.01));
}

return $res;
}

Для бухгалтерского:


#!perl -w
use strict;

open(FD, "data.txt");
open(FR, ">std_b.txt");
while(<FD>) {
chomp;
my $r = okr_b($_);
print FR "$_ $r\n";
}
close(FR);
close(FD);

sub okr_b {
my $n1 = shift;

my $pos_comma = index($n1, '.');
my $res = substr($n1, 0, $pos_comma + 3);
my $prerounded_digit    = substr($n1, $pos_comma + 2, 1);
my $rounded_digit       = substr($n1, $pos_comma + 3, 1);
my $afterrounded_digits = substr($n1, $pos_comma + 4, 1);
my $prerounded_digit_is_odd = (($prerounded_digit % 2) != 0);
my $afterrounded_digits_presents = ($afterrounded_digits > 0);

if (
($rounded_digit > 5)
or
(
($rounded_digit == 5)
and
($prerounded_digit_is_odd or $afterrounded_digits_presents)
)
)
{

$res = sprintf("%3.2f", ($res + 0.01));

}

return $res;
}

PS. По концепции ранних MySQL, он доверял политику округления системным библиотекам, поэтому я подозреваю, что результат будет зависеть от операционки. Если есть возможность, попробуй на разных ОС.


25-04-2006 14:01
Может быть, я не очень хорошо разбираюсь в Perl, но, вроде бы, приведенный код будет создавать всего 1 миллион значений (100 * 10000). Где еще 2 порядка? ;-)

Я просто хочу попробовать (если время позволит) MySQL версии 5.0 (которая у меня сейчас стоит). Но для чистоты эксперимента хочу проверить на тех же самых данных (насколько это возможно).
 Geo


25-04-2006 13:47
сообщение от автора материала
Разумеется, числа идут не все подряд. Вообще, сначала я тестировал подряд миллион чисел от 0.0000 до 100.0000, и в принципе получил все что хотел. Но на некоторых функциях ошибки проявлялись лишь на больших числах, поэтому хотелось, сохранив плотное тестирование малых значений, слегка заглянуть и в большие. В окончательном варианте тестовых данных целая часть растет по экспоненте, но для каждого значения целой части отрабатываются все значения дробной от x.0000 до x.9999

Короче говоря:


#!perl -w

open(F, ">data.txt");
foreach my $i (0..99) {
my $k = int(1.205**$i) - 1 + $i;
for(my $j = 0; $j<=0.99999; $j = $j + 0.0001) {
printf F "%.4f\n", $k+$j;
}

}
close(F);





25-04-2006 12:18
Не уверен в чистоте эксперимента (как минимум, для самоделок, основанных на прибавлении бесконечно малого), так как на других задачах округления (до другой цифры) результаты исследования могут отличаться.

Впрочем, если тестирование проводилось для определения эффективности функций при бухгалтерских расчетах, тогда замечание снимается.

Кстати, не совсем понял методику построения файла с исходными данными. Вроде бы чисел 100 миллионов, а в то же время приведенные чсила отличаются друг от друга на 0.0001, в то время как максимальное число равно 104164096.9999. Если понимать тупо в лоб, то должно получиться больше 10^12 чисел. Можно этот момент поподробнее? Чтобы тестировать другие функции на тех же самых данных.
 Geo


Добавьте свое cообщение

Вашe имя:  [Войти]
Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
контрольный вопрос:
Какой месяц идет после марта?
в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.

Оценка содержания
 
Содержит полезные и(или) интересные сведения
 
Ничего особенно нового и интересного
 
Написано неверно (обязательно укажите почему)


Оценка стиля изложения
 
Все понятно, материал читается легко
 
Есть неясности в изложении
 
Непонятно написано, трудно читается

Текст:
Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования