ЗАОСТРИМ НА ОКРУГЛЕНИИ

Aleksey Tikhonov
Altsoph’s blog
Published in
6 min readFeb 7, 2008

По итогам некоторой работы решил наваять небольшую статью по поводу разных методов округлений и их реализаций в PHP, Excel, OpenOffice. Думаю, эта информация может пригодиться кому-то еще :)

Методы округления в Excel, PHP и не только

Введение

При решении задач мат.моделирования и автоматизации бизнес-процессов очень часто возникают погрешности результата, связанные с неочевидностью выбора способов округления, используемых при написании кода.

Так, заказчик почти никогда не может явно сформулировать требования к применяемым в расчетах способам округления, и, зачастую, просто использует расчет, выполненный в Excel-е, как эталон, не задумываясь, как именно и почему именно так происходит округление в том или ином случае.

В такой ситуации полезно:

  • точно представлять существующие стандартные методы округления,
  • уметь по расчету Excel или другому <эталонному> расчету определять использованные в нем методы округления,
  • уметь воспроизводить желаемый способ округления средствами языка, используемого для разработки.

В данной статье мы расскажем о стандартных способах округления, проанализируем пакет MS Excel 2003, пакет OpenOffice Calc 2.3.0 и язык PHP на предмет имеющихся в них функций округления и их соответствия стандартным. Попутно будут предложены примеры реализаций недостающих функций.

Виды округлений

Стандарт IEEE 754 описывает семь методов округления чисел:

  • round-down — усечение, округление по направлению к нулю,
  • round-half-up — арифметическое округление (если отбрасываемые цифры больше либо равны 0.5, происходит округление вверх (от нуля), иначе цифры просто отбрасываются),
  • round-half-even — банковское округление, (если отбрасываемые цифры больше 0.5, происходит округление вверх (от нуля), если меньше 0.5 — цифры отбрасываются, если отбрасываемая часть равна 0.5, то округление происходит в зависимости от четности предпоследней цифры),
  • round-ceiling — округление к плюс бесконечности,
  • round-floor — округление к минус бесконечности,
  • round-half-down — подобно арифметическому (round-half-up), пятерка округляется вниз,
  • round-up — округление от нуля.

Первые пять методов являются, согласно стандарту, обязательными к реализации, а последние два — опциональными.

Тестовая выборка, однозначно различающая методы

Для определения соответствия функции, реализующей округление, некоторому стандартному методу округления удобно воспользоваться специальной тестовой выборкой чисел. Предлагаемый набор из 4 чисел позволяет однозначно различить перечисленные в стандарте семь методов округления.

Рассмотрим следующую таблицу (в предположении, что округления производились до 1 знака после запятой):

Тип округления 0.35 -0.25 -0.41 -0.59 round_down 0,3–0,2–0,4–0,5 round_up 0,4–0,3–0,5–0,6 round_half_up 0,4–0,3–0,4–0,6 round_half_even 0,4–0,2–0,4–0,6 round_ceiling 0,4–0,2–0,4–0,5 round_floor 0,3–0,3–0,5–0,6 round_half_down 0,3–0,2–0,4–0,6

Не будем проводить доказательства того, что набор из 4 чисел является минимальным для решения задачи различения методов, покажем лишь, что в данном конкретном наборе нельзя удалить ни одно из чисел:

  • При удалении числа 0.35 <склеиваются> методы round_half_even и round_half_down.
  • При удалении числа -0.25 <склеиваются> методы round_half_up и round_half_even.
  • При удалении числа -0.41 <склеиваются> методы round_up и round_half_up.
  • При удалении числа -0.59 <склеиваются> методы round_half_even и round_ceiling.

Отметим, что данный набор чисел не может использоваться для доказательства корректности той или иной функции округления, а только для определения её типа и только в том случае, если заранее известно, что функция реализует один из 7 вышеописанных методов округления.

Анализ существующих функций округления в MS Excel 2003

Изучение и понимание отличий функций округления, доступных в MS Excel, представляется нам важной задачей, т.к. большинство расчетных прототипов, входящих в ТЗ на задачи автоматизации и моделирования, реализуются именно средствами MS Office. При этом важно понимать, что проверка корректности выполнения ТЗ обычно производится путем сверки результатов с результатами прототипа. Таким образом, умение воспроизводить результаты работы методов округления, используемых в каждом конкретном случае, является необходимыми для получения адекватных расчетных результатов.

Анализ справочника функций MS Office Excel 2003 позволяет составить следующий список стандартных функций, реализующих различные виды округления: ОКРУГЛ, ОКРВВЕРХ, ОКРВНИЗ, ОКРУГЛВВЕРХ, ОКРУГЛВНИЗ, ОТБР, ЦЕЛОЕ.

Функция 0.35 -0.25 -0.41 -0.59 Результат анализа ОКРУГЛ 0.4–0.3–0.4–0.6совпадает с round_half_up ОКРВВЕРХ 0.4–0.3–0.5–0.6совпадает с round_up ОКРВНИЗ 0.3–0.2–0.4–0.5совпадает с round_down ОКРУГЛВВЕРХ 0.4–0.3–0.5–0.6совпадает с round_up ОКРУГЛВНИЗ 0.3–0.2–0.4–0.5совпадает с round_down ОТБР 0.3–0.2–0.4–0.5совпадает с round_down ЦЕЛОЕ 0–1–1–1не позволяет использовать точность. ЦЕЛОЕ(X*10)/10 0.3–0.3–0.5–0.6совпадает с round_floor

Таким образом, получаем следующие различные наборы функций:

  • ОКРУГЛ использует метод round_half_up,
  • ОКРВВЕРХ, ОКРУГЛВВЕРХ используют round_up (различаются способом указания аргументов),
  • ОКРВНИЗ, ОКРУГЛВНИЗ, ОТБР используют round_down (различаются способом указания аргументов),
  • ЦЕЛОЕ использует round_floor, но не позволяет использовать точность.

Следовательно, пользователям MS Excel доступны 4 из 7 рассматриваемых методов округления:

  • round_up (округление от нуля),
  • round_down (округление к нулю)
  • round_half_up (арифметическое округление),
  • round_floor (округление к минус бесконечности).

Реализация недостающих методов округления в MS Excel 2003

Для реализации недостающих методов округления нам потребуется математическая функция sgn (signum). В языке формул MS Excel она реализована функцией ЗНАК.

Итак, опишем на псевдокоде, близком к языку формул MS Excel, формулы, позволяющие получить желаемые методы округления.

ROUND_CEILING (X,D) = ЕСЛИ(ЗНАК(X)>0;ОКРУГЛВВЕРХ(X;D);ОКРУГЛВНИЗ(X;D))

ROUND_FLOOR (X,D) = ЕСЛИ(ЗНАК(X)<0;ОКРУГЛВВЕРХ(X;D);ОКРУГЛВНИЗ(X;D))

ROUND_HALF_DOWN(X,D) = ЕСЛИ (2*ОКРУГЛВВЕРХ(X;D) = ОКРУГЛВВЕРХ(2* X;D);ОКРУГЛ(X;D);ОКРУГЛВНИЗ(X;D))

ROUND_HALF_EVEN(X,D) = ЕСЛИ (ОКРУГЛ(X;D) = ROUND_HALF_DOWN(X,D);ОКРУГЛ(X;D);ЕСЛИ( ОКРУГЛВНИЗ(X;D)/2 = ОКРУГЛВНИЗ(ОКРУГЛВНИЗ(X;D)/2;D); ОКРУГЛВНИЗ(X;D); ОКРУГЛВВЕРХ(X;D) ) )

Данные формулы не являются единственным или наилучшим способом реализации указанных методов округления, а являются лишь иллюстрацией возможности их реализации через имеющиеся стандартные методы MS Excel.

Сравнение округлений в OpenOffice Calc 2.3.0 и в MS Excel 2003

Изучение справочника функций языка формул OpenOffice Calc позволило составить такой список функций округления: TRUNC, ROUND, ROUNDDOWN, ROUNDUP, CEILING, FLOOR, MROUND, INT. При этом функции CEILING и FLOOR имеют 2 режима, один из которых (mode=1) декларирован как режим совместимости с MS Excel.

Функция 0.35 -0.25 -0.41 -0.59 Тип округления Соответстие в Excel TRUNC 0,3–0,2–0,4–0,5round_down ОТБР ROUND 0,4–0,3–0,4–0,6round_half_up ОКРУГЛ ROUNDDOWN 0,3–0,2–0,4–0,5round_down ОКРУГЛВНИЗ ROUNDUP 0,4–0,3–0,5–0,6round_up ОКРУГЛВВЕРХ CEILING (mode=0) 0,4–0,2–0,4–0,5round_ceiling Отустствует CEILING (mode=1) 0,4–0,3–0,5–0,6round_up ОКРВВЕРХ FLOOR (mode=0) 0,3–0,3–0,5–0,6round_floor Отсутствует FLOOR (mode=1) 0,3–0,2–0,4–0,5round_down ОКРВНИЗ MROUND 0,4–0,3–0,4–0,6round_half_up Отсутствует INT 0–1–1–1не позволяет использовать точностьЦЕЛОЕ INT(X*10)/10 0,3–0,3–0,5–0,6round_floor ЦЕЛОЕ(X*10)/10

Таким образом, OpenOffice Calc содержит больше различных функций округления, а именно функции CEILI NG и FLOOR, реализующие в режиме (mode=0) методы round_ceiling и round_floor, соответственно.

Анализ существующих функций округления в PHP

Спецификация языка PHP предлагает 4 способа округления чисел: intval, round, floor, ceil. Существуют также менее очевидные способы округления чисел в PHP путем форматирования числа в строку и преобразования его обратно в число. Для такого форматирования могут использоваться стандартные php-функции number_format и sprintf.

Поскольку функции ceil, floor и intval не позволяют указывать требуемую точность округления, для них были написаны обертки (соответственно, p_ceil, p_floor и p_intval).

Функция 0.35 -0.25 -0.41 -0.59 Результат анализа round 0.4–0.3–0.4–0.6совпадает с round_half_up ceil 1000не позволяет использовать точность floor 0–1–1–1не позволяет использовать точность intval 0000не позволяет использовать точность p_ceil 0.4–0.2–0.4–0.5совпадает с round_ceiling p_floor 0.3–0.3–0.5–0.6совпадает с round_floor p_intval 0.3–0.2–0.4–0.5совпадает с round_down number_format 0.4–0.3–0.4–0.6совпадает с round_half_up sprintf 0.4–0.3–0.4–0.6совпадает с round_half_up

Таким образом, получаем следующие различные наборы функций:

  • round, number_format, sprintf используют метод round_half_up,
  • ceil (p_ceil) использует метод round_ceiling,
  • floor (p_floor) использует метод round_floor,
  • intval (p_intval) использует метод round_down.

Следовательно, разработчикам на PHP доступно 4 из 7 рассматриваемых методов округления:

  • round_floor (округление к минус бесконечности),
  • round_ceiling (округление к плюс бесконечности),
  • round_half_up (арифметическое округление),
  • round_down (округление к нулю).

Кроме того, intval (и как следствие, p_intval) имеет сильные ограничения на размер значения аргумента — максимальное допустимое значение аргумента intval равно MAXINT (точное значение зависит от системы). По этой причине не рекомендуется использовать функцию intval для реализации округлений при решении расчетных задач.

Реализация недостающих методов округления в PHP

Как указывалось выше, для нормального использования функций floor и ceil потребовалось реализовать функции-обертки, позволяющие указывать точность округления (количество знаков после запятой).

Вот они:

function p_ceil($val, $d)
{
return ceil($val*pow(10,$d))/pow(10,$d);
}

function p_floor($val, $d)
{
return floor($val*pow(10,$d))/pow(10,$d);
}

Кроме того, в PHP отсутствует реализация математической функции sgn. Нам потребуется своя:

function sgn($x)
{
return $x?($x>0?1:-1):0;
}

Т еперь, имея p_ceil, p_floor и round (как реализации, соответственно, round_ceiling, round_floor и round_half_up) и собственную реализацию sgn, мы можем воспроизвести недостающие методы округления следующим образом:

function round_down($num, $d = 0)
{
// подтасовкой знака меняем направление округления
// с минус бесконечности на ноль
return sgn($num)*p_floor(abs($num), $d);
}

function round_up($num, $d = 0)
{
// подтасовкой знака меняем направление округления
// с плюс бесконечности на <от нуля>
return sgn($num)*p_ceil(abs($num), $d);
}

function round_half_down($num, $d = 0)
{
// Если последняя цифра > 5
// то округляем вверх
// иначе округляем вниз
return((2*round_up($num,$d)==round_up(2*$num,$d))
? round($num,$d)
: round_down($num,$d) );
}

function round_half_even($num, $d = 0)
{
// Если округляемая цифра !=5, то
// всё ясно, округление стандартное
if( round($num,$d) == round_half_down($num,$d) )
return round($num,$d);

// Получаем предпоследнюю цифру
$pre_digit = round_down( $num, $d );

// Если она четная
if( ($pre_digit/2) == round_down($pre_digit/2,$d) )
// то округляем вниз
return round_down($num, $d);
else
// иначе округляем вверх
return round_up($num, $d);
}

Данный код не является единственным или наилучшим способом реализации указанных методов округления, а являются лишь иллюстрацией возможности их реализации через имеющиеся стандартные функции PHP.

Полезные ссылки по теме

Полезные ссылки по теме

Оригинал статьи и прилагающийся код доступны тут
http://altsoph.ru/stuff/prjs/php/round_me_out.zip

Прототип на PHP, использованный при написании статьи
http://altsoph.ru/stuff/prjs/php/round_me_out.php

Описание стандарта IEEE 754
http://en.wikipedia.org/wiki/IEC_559

Выдержки из стандарта IEEE 754
http://www2.hursley.ibm.com/decimal/

Очень полезная статья “Загадки округления” про проблемы округления
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1217

Арифметика приближенных чисел
http://www.clicktour.ru/articles/15/1001515/1001515a13.htm

Статья “IEEE754-тика угрожает человечеству”
http://yur.ru/science/computer/IEEE754.htm

Правила округления СТ СЭВ 543–77
http://www.metrologie.ru/usefullinform-okrugl.htm
http://diamondsteel.ru/useful/handbook/6.html

Справочник математических функций OpenOffice Calc
http://wiki.services.openoffice.org/wiki/Documentation/How_Tos/Calc:_Mathematical_functions

--

--