Модераторы: korob2001, ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> нюанс printf(), неоднозначность округления 
V
    Опции темы
ginnie
Дата 20.10.2008, 17:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Комодератор
Сообщений: 1287
Регистрация: 6.1.2008
Где: Москва

Репутация: 38
Всего: 49



Уважаемые коллеги!

Раньше не сталкивался со следующим нюансом округления при помощи printf():

Код

printf("%.0f",100.50), $/;


выводит 100

Код

printf("%.0f",101.50), $/;


выводит 102.

Кто скажет, почему так происходит  smile ?


--------------------
Написать код, понятный компьютеру, может каждый, но только хорошие программисты пишут код, понятный людям. (Мартин Фаулер. Рефакторинг)
PM MAIL Skype Jabber   Вверх
arto
Дата 20.10.2008, 18:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1495
Регистрация: 31.10.2004

Репутация: 38
Всего: 40



переход строка-число.

# for i in {0..10}; do perl -le 'printf "%.0f\n",$ARGV[0]' $i.50; done
0
2
2
4
4
6
6
8
8
10
10
#
PM MAIL ICQ   Вверх
KSURi
Дата 20.10.2008, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 887
Регистрация: 8.6.2006
Где: Russia

Репутация: 20
Всего: 27



banker's rounding


--------------------
Died at Life.pl line 21
PM Jabber   Вверх
ginnie
Дата 20.10.2008, 19:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Комодератор
Сообщений: 1287
Регистрация: 6.1.2008
Где: Москва

Репутация: 38
Всего: 49



KSURi, молодец, верно указал источник неоднозначности!
Цитата

банковское округление (англ. banker's rounding) — округление половины (N+1 знак = 5) к ближайшему чётному. В этом случае исчезает систематическая ошибка округления при суммировании большого количества чисел.


P.S. сам раньше нигде про этот нюанс информации не встречал (если кто знает, где про это есть в документации - сообщите). Сегодня случайно увидел вопрос на сайте PerlMonks (A bug of sprintf or not?).

P.P.S. KSURi, ты знал, или тоже на PerlMonks увидел?

Это сообщение отредактировал(а) ginnie - 20.10.2008, 19:33


--------------------
Написать код, понятный компьютеру, может каждый, но только хорошие программисты пишут код, понятный людям. (Мартин Фаулер. Рефакторинг)
PM MAIL Skype Jabber   Вверх
shamber
Дата 21.10.2008, 09:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1422
Регистрация: 5.9.2006
Где: Россия

Репутация: 17
Всего: 18



ginnie, а я сталкивался smile Когда округления писать нужно было 
PM MAIL Jabber   Вверх
KSURi
Дата 21.10.2008, 10:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 887
Регистрация: 8.6.2006
Где: Russia

Репутация: 20
Всего: 27



ginnie, видел на PerlMonks, только другой топик. Сталкиваться не приходилось пока.


--------------------
Died at Life.pl line 21
PM Jabber   Вверх
IceSunrise
Дата 9.2.2009, 11:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 61
Регистрация: 7.4.2008
Где: Санкт-Петербург

Репутация: 3
Всего: 3



Здесь есть еще один нюанс. Нужно принимать во внимание еще и машинное представление чисел с плавающей точкой.
Код

my $x=0.05;
for (1..20)
{
    printf ("%f -> %.1f\n",$x,$x);
    $x+=0.05;
}


Получаем вывод:
Код

0.050000 -> 0.1
0.100000 -> 0.1
0.150000 -> 0.2
0.200000 -> 0.2
0.250000 -> 0.2
0.300000 -> 0.3
0.350000 -> 0.3
0.400000 -> 0.4
0.450000 -> 0.4
0.500000 -> 0.5
0.550000 -> 0.5
0.600000 -> 0.6
0.650000 -> 0.7
0.700000 -> 0.7
0.750000 -> 0.8
0.800000 -> 0.8
0.850000 -> 0.9
0.900000 -> 0.9
0.950000 -> 1.0
1.000000 -> 1.0


Следуя алгоритму банковского округления:
Цитата


  • Decide which is the last digit to keep.
  • Increase it by 1 if the next digit is 6 or more, or a 5 followed by one or more non-zero digits.
  • Leave it the same if the next digit is 4 or less
  • Otherwise, if all that follows the last digit is a 5 and possibly trailing zeroes; then increase the rounded digit if it is currently odd; else, if it is already even, leave it alone.



Мы должны получить:
0.25 -> 0.2 (есть)
0.35 -> 0.4
0.65 -> 0.6

Проблему увидим, если посмотрим на округляемое число пристальнее:
Код

printf("%.20f\n", 0.35);


Код

0.34999999999999997780


Все честно, с точки зрения округления...

Модуль Math::BigFloat помогает решить проблему.
Код

use Math::BigFloat;
$f = new Math::BigFloat '0.35';
$f->ffround(-1);
print "F='$f'\n";

PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0858 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.