Округлить целое число до ближайшего кратного 5 в php

Интерактивное программирование[править]

Особенно удобная возможность, предоставляемая Python — это возможность быстро проверить как работают инструкции или выражения в интерактивном режиме. Такие интерактивные оболочки называются shell. Мы уже пользовались ей как частью IDLE. Остановимся сейчас на ней поподробнее.

Interactive Shellправить

Программа, с которой мы работали в первом уроке, может быть напечатана строчка за строчкой с тем же конечным результатом:

>>> v0 = 5
>>> g = 9.81
>>> t = 0.6
>>> y = v0 * t - 0.5 * g * t ** 2
>>> print (y)
1.2342

Мы можем легко задать новое значение переменной, например для v0, и всегда способны проконтролировать, что новое значение присвоено, введя имя переменной напрямую или через инструкцию print::

>>> v0 = 6
>>> v0
6
>>> print (v0)
6

Следующий шаг — обновить выражения, в которых еще содержится старое значение, то есть нашу формулу для y. В таком редакторе как IPython можно найти предыдущее выражение, с помощью кнопок со стрелками. О возможностях этого редактора мы еще поговорим. А пока:

>>> y = v0 * t - 0.5 * g * t ** 2
>>> y
1.8341999999999996
>>> print (y)
1.8342

Причина, по которой мы получили сейчас два разных результата, в том, что, если вы просто пишите y, то вы увидите все знаки после точки, находящиеся в памяти компьютера (16), в то время как инструкция print ограничивает число знаков и, таким образом, дает ответ с достаточной точностью. Впрочем, в некоторых версиях Python автоматически округляет значение переменной.

Преобразование типаправить

Если вы ранее работали с другими языками программирования, то могли заметить, что мы пишем программы не заботясь о задании типов переменных (вернее даже типов объектов, на которые ссылаются переменные). Однако в этом уроке мы встретились с вопросом типов объектов, когда изучали ошибку целочисленного деления, которая научила нас более внимательно относиться к типам объектов. Следующий пример иллюстрирует функцию type( ) и то, как мы можем изменять тип объекта. Для начала, создадим объект типа int, на который будет ссылаться имя С:

>>> C = 21
>>> type(C)
<type 'int'>

Теперь мы преобразуем наш объект С типа int к соответствующему объекту типа float:

>>> C = float(C)     #  type  conversion
>>> type(C)
<type 'float'>
>>> C
21.0

В инструкции C = float(C) мы создали новый объект из оригинального объекта с тем же именем. Теперь имя С ссылается на новый объект. Изначальный объект типа int со значением 21 теперь недоступен, поскольку у него больше нет имени и автоматически удаляется из памяти.

Мы также можем сделать и обратное, то есть конвертировать объект типа float к объекту типа int:

>>> C = 20.9
>>> type(C)
<type 'float'>
>>> D = int(C) #  type  conversion
>>> type(D)
<type 'int'>
>>> D
20  # decimals  are  truncated  :-/

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

>>> round(20.9)
21.0
>>> int(round(20.9))
21

Округление до целого числа

Все функции в качестве основного параметра используют значение с плавающей точкой. Например:


При выполнении каждой функции, получим следующие значения:

echo ceil($x); // 124 (к большему целому значению) echo floor($x); // 123 (к меньшему целому значению) echo round($x); // 123 (математически к ближайшему)

При математическом округлении, учитывается значение числа округляемой части. Если оно меньше 5, округляется к меньшему. Если равно или больше 5, округление будет выполнено в большую сторону. В данном случае функция round округляет к меньшему значению, т.к. значение дробной части меньше 0.5. Если использовать значение больше, например:

В результате округления, получим:

echo round($x); // 124

Особенности использования функции ФИКСИРОВАННЫЙ в Excel

Рассматриваемая функция имеет следующий синтаксис:

=ФИКСИРОВАННЫЙ(число;;)

Описание аргументов:

  • число – обязательный аргумент, характеризующий числовое значение, которое будет округлено в соответствии с указанными условиями и в последствии преобразовано в текстовый формат данных.
  • – количество знаков после запятой, которые будут отображены после выполнения операции округления. Параметр необязателен для заполнения.
  • – необязательный аргумент, характеризующий логические значения:
  1. ИСТИНА – разделители тысяч в возвращаемом текстовом представлении числа будут отсутствовать.
  2. ЛОЖЬ – разделители тысяч будут включены в возвращаемое значение.

Примечания:

  1. Разделитель тысяч используется для удобства представления больших значений. Например, число 1000 с разделителем будет иметь вид «1 000». Если аргумент явно не указан или содержит значение ЛОЖЬ, в возвращаемом значении будут содержаться разделители.
  2. Наличие разделителей тысяч в числах не является помехой для преобразования текстовых представлений чисел к числовым значениям. Например, результатом выполнения функции =ФИКСИРОВАННЫЙ(1250,5;1;ЛОЖЬ) будет значение «1 250,5», которое может быть преобразованой функцией =ЧЗНАЧ(«1 250,5») в значение 1250,5.
  3. Если в качестве аргумента число были переданы текстовые данные, результатом выполнения функции ФИКСИРОВАННЫЙ будет код ошибки #ЗНАЧ!.
  4. Если аргумент явно не указан, предполагается, что он принимает значение 2 (то есть выполняется округление до двух знаков после запятой).
  5. Если – отрицательное число, округление производится до указанного числа знаков перед запятой. Например, =ФИКСИРОВАННЫЙ(123;-2) вернет значение «100».
  6. Если – отрицательное число, модуль которого превышает значение количетсва знаков до запятой, будет возвращено значение 0. Например, =ФИКСИРОВАННЫЙ(123;-6) вернет значение «0».
  7. Максимальное значение аргумента составляет 127 (несмотря на то, что в Excel максимальное количество значащих цифр в числе составляет 15).
  8. Рассматриваемая функция выполняет округление чисел в соответствии с правилами математического округления. При отбросе последней цифры числа, предыдущая увеличивается на единицу, если отбрасываемая находится в диапазоне значений от 5 до 9 включительно. Если отбрасываемая цифра находится в диапазоне значений от 0 до 4 включительно, предыдущая цифра остается без изменений. Например, при округлении чисел 12,41 и 12,46 до одного знака после запятой будут получены значения 12,4 и 12,5 соответственно.

Округление дробного числа до x знаков после запятой

Вторым параметром функции round является количество знаков после запятой, которое может принимать отрицательные значения.

Задание способа округления функции round

Третий параметр (константа) функции round задает способ округления

PHP_ROUND_HALF_UP — Округляет val в большую сторону от нуля до precision десятичных знаков, если следующий знак находится посередине. Т.е. округляет 1.5 в 2 и -1.5 в -2.

PHP_ROUND_HALF_DOWN — Округляет val в меньшую сторону к нулю до precision десятичных знаков, если следующий знак находится посередине. Т.е. округляет 1.5 в 1 и -1.5 в -1.

PHP_ROUND_HALF_EVEN — Округляет val до precision десятичных знаков в сторону ближайшего четного знака.

PHP_ROUND_HALF_ODD — Округляет val до precision десятичных знаков в сторону ближайшего нечетного знака.

Функции выполняющие сложные математические операции

Начнем с самого простого. Функция MOD (x, y) возвращает остаток от деления x на y. Например:

SELECT MOD (10, 3) Результат: 1

Следующая функция EXP (x), которая возвращает значение e (Число Эйлера) возведенное в степень x. Или научным языком, возвращает экспоненту числа. Пример:

SELECT EXP (3) Результат: 20.085536923187668

Далее рассмотрим функцию LOG (x), которая возвращает натуральный логарифм числа x. Пример:

SELECT LOG (10) Результат: 2.302585092994046

Для получения логарифма числа x, для произвольной основы логарифма y можно пользоваться формулой LOG (x)/LOG (y). Например:

SELECT LOG (8)/LOG (2) Результат: 3

Для получения десятичного логарифма числа x существует функция LOG10 (x). Пример:

SELECT LOG10 (100) Результат: 2

Для возведения в степень в языке SQL есть целых две функции: POW (x, y) и POWER (x, y). Возвращают число x возведенное в степень y. Пример:

SELECT POW (2, 3) Результат: 8 SELECT POWER (3, 2) Результат: 9

А функция SQRT (x) вычисляет квадратный корень числа x. Пример:

SELECT SQRT (16) Результат: 4

Чтобы использовать в своих вычисления число «пи» в SQL есть функция PI (), которая возвращает значение этого числа:

SELECT PI () Результат: 3.141593

Функции из библиотеки Math

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

math.ceil

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

Любая дробь находится между двумя целыми числами. Например, 2.3 лежит между 2 и 3. Функция ceil() определяет большую сторону и возводит к нему результат преобразования. Например:

Алгоритм определяет большую границу интервала с учетом знака:

math.floor

действует противоположно – округляет дробное значение до ближайшего целого, которое меньше или равно исходному. Округление происходит в меньшую сторону (вниз):

При округлении учитывается знак перед данными.

math.trunc

Функция характеризуется отбрасыванием дробной части. После преобразования получается целое значение без учета дроби. Такой алгоритм не является округлением в арифметическом смысле. В Пайтон просто игнорируется дробь независимо от ее значения:

Избавиться от дроби можно без подключения модуля. Для этого есть стандартная функция Она преобразовывает дробные числа в целые путем игнорирования дроби.

Различие округления в Python 2 и Python 3

В Python 2 и Python 3 реализованы разные принципы округления.

В Python 2 используется арифметическое округление. В нем наблюдается большое количество погрешностей, что приводит к неточностям в процессе вычислений.

Во втором Python есть только 4 цифры, которые ведут к преобразованию к меньшему значению – 1, 2, 3 и 4. Также 5 цифр, которые приводят к большему значению – 5, 6, 7, 8, 9. Такое неравное распределение ведет к тому, что погрешность постоянно нарастает.

Python 2 по правилам арифметического округления преобразует число 5,685 в 5,68 до второго знака. Такая погрешность связана с тем, что десятичные цифры float в двоичном коде невозможно корректно представить.

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

2,5 по правилам банковского преобразования будет равно 2, а 3,5 = 4 (значения возводятся к близкому четному). Минимизировать погрешности можно благодаря практически равной вероятности, что перед пятеркой будет четное или нечетное число.

Архив блога

  • ► 

    2018

    (2)

    • ► 

      апреля

      (1)

      ► 

      27 апр

      (1)

    • ► 

      февраля

      (1)

      ► 

      08 фев

      (1)

  • ► 

    2017

    (5)

    • ► 

      декабря

      (2)

      ► 

      13 дек

      (2)

    • ► 

      июля

      (1)

      ► 

      14 июл

      (1)

    • ► 

      марта

      (1)

      ► 

      22 мар

      (1)

    • ► 

      января

      (1)

      ► 

      23 янв

      (1)

  • ► 

    2016

    (7)

    • ► 

      декабря

      (1)

      ► 

      29 дек

      (1)

    • ► 

      октября

      (2)

      ► 

      26 окт

      (1)

      ► 

      13 окт

      (1)

    • ► 

      сентября

      (1)

      ► 

      26 сен

      (1)

    • ► 

      марта

      (1)

      ► 

      02 мар

      (1)

    • ► 

      февраля

      (1)

      ► 

      19 фев

      (1)

    • ► 

      января

      (1)

      ► 

      27 янв

      (1)

  • ► 

    2015

    (11)

    • ► 

      октября

      (1)

      ► 

      23 окт

      (1)

    • ► 

      сентября

      (1)

      ► 

      24 сен

      (1)

    • ► 

      июля

      (3)

      ► 

      14 июл

      (1)

      ► 

      13 июл

      (1)

      ► 

      10 июл

      (1)

    • ► 

      апреля

      (1)

      ► 

      23 апр

      (1)

    • ► 

      марта

      (2)

      ► 

      27 мар

      (1)

      ► 

      18 мар

      (1)

    • ► 

      февраля

      (1)

      ► 

      04 фев

      (1)

    • ► 

      января


      (2)

      ► 

      28 янв

      (1)

      ► 

      22 янв

      (1)

  • ► 

    2014

    (6)

    • ► 

      декабря

      (1)

      ► 

      18 дек

      (1)

    • ► 

      ноября

      (1)

      ► 

      10 ноя

      (1)

    • ► 

      сентября

      (1)

      ► 

      26 сен

      (1)

    • ► 

      июня

      (1)

      ► 

      03 июн

      (1)

    • ► 

      мая

      (1)

      ► 

      21 мая

      (1)

    • ► 

      февраля

      (1)

      ► 

      18 фев

      (1)

  • ► 

    2013

    (24)

    • ► 

      декабря

      (2)

      ► 

      17 дек

      (1)

      ► 

      12 дек

      (1)

    • ► 

      ноября

      (1)

      ► 

      08 ноя

      (1)

    • ► 

      октября

      (5)

      ► 

      29 окт

      (1)

      ► 

      28 окт

      (1)

      ► 

      15 окт

      (1)

      ► 

      04 окт

      (2)

    • ► 

      августа

      (2)

      ► 

      13 авг

      (1)

      ► 

      01 авг

      (1)

    • ► 

      июля

      (1)

      ► 

      07 июл

      (1)

    • ► 

      июня

      (6)

      ► 

      25 июн

      (1)

      ► 

      21 июн

      (1)

      ► 

      20 июн

      (1)

      ► 

      19 июн

      (1)

      ► 

      14 июн

      (1)

      ► 

      11 июн

      (1)

    • ► 

      мая

      (1)

      ► 

      06 мая

      (1)

    • ► 

      марта

      (2)

      ► 

      31 мар

      (1)

      ► 

      12 мар

      (1)

    • ► 

      февраля

      (2)

      ► 

      18 фев

      (1)

      ► 

      07 фев

      (1)

    • ► 

      января

      (2)

      ► 

      23 янв

      (1)

      ► 

      09 янв

      (1)

  • ► 

    2012

    (34)

    • ► 

      декабря

      (2)

      ► 

      03 дек

      (1)

      ► 

      01 дек

      (1)

    • ► 

      ноября

      (7)

      ► 

      25 ноя

      (1)

      ► 

      22 ноя

      (2)

      ► 

      15 ноя

      (1)

      ► 

      08 ноя

      (1)

      ► 

      03 ноя

      (1)

      ► 

      02 ноя

      (1)

    • ► 

      октября

      (1)

      ► 

      18 окт

      (1)

    • ► 

      сентября

      (4)

      ► 

      21 сен

      (1)

      ► 

      10 сен

      (3)

    • ► 

      августа

      (2)

      ► 

      25 авг

      (1)

      ► 

      22 авг

      (1)

    • ► 

      июля

      (2)

      ► 

      27 июл

      (1)

      ► 

      03 июл

      (1)

    • ► 

      июня

      (5)

      ► 

      12 июн

      (2)

      ► 

      11 июн

      (1)

      ► 

      08 июн

      (1)

      ► 

      07 июн

      (1)

    • ► 


      мая

      (10)

      ► 

      27 мая

      (1)

      ► 

      24 мая

      (2)

      ► 

      16 мая

      (1)

      ► 

      14 мая

      (2)

      ► 

      12 мая

      (1)

      ► 

      11 мая

      (1)

      ► 

      10 мая

      (2)

    • ► 

      января

      (1)

      ► 

      30 янв

      (1)

  • ▼ 

    2011

    (19)

    • ▼ 

      ноября

      (2)

      ► 

      30 ноя

      (1)

      • ▼ 

        10 ноя

        (1)

        PHP, округление до десятков и сотен в большую сторону

    • ► 

      сентября

      (7)

      ► 

      23 сен

      (1)

      ► 

      13 сен

      (1)

      ► 

      12 сен

      (2)

      ► 

      09 сен

      (2)

      ► 

      01 сен

      (1)

    • ► 

      августа

      (4)

      ► 

      30 авг

      (1)

      ► 

      27 авг

      (1)

      ► 

      26 авг

      (1)

      ► 

      24 авг

      (1)

    • ► 

      июля

      (1)

      ► 

      25 июл

      (1)

    • ► 

      июня

      (1)

      ► 

      01 июн

      (1)

    • ► 

      апреля

      (2)

      ► 

      27 апр

      (1)

      ► 

      25 апр

      (1)

    • ► 

      марта

      (2)

      ► 

      29 мар

      (1)

      ► 

      19 мар

      (1)

  • ► 

    2010

    (59)

    • ► 

      декабря

      (3)

      ► 

      24 дек

      (1)

      ► 

      12 дек

      (1)

      ► 

      11 дек

      (1)

    • ► 

      ноября

      (5)

      ► 

      22 ноя

      (2)

      ► 

      13 ноя

      (2)

      ► 

      03 ноя

      (1)

    • ► 

      октября

      (3)

      ► 

      12 окт

      (1)

      ► 

      11 окт

      (1)

      ► 

      01 окт

      (1)

    • ► 

      сентября

      (3)

      ► 

      16 сен

      (1)

      ► 

      15 сен

      (2)

    • ► 

      августа

      (3)

      ► 

      28 авг

      (1)

      ► 

      17 авг

      (1)

      ► 

      02 авг

      (1)

    • ► 

      июля

      (4)

      ► 

      14 июл

      (1)

      ► 

      13 июл

      (1)

      ► 

      01 июл

      (2)

    • ► 

      июня

      (5)

      ► 

      29 июн

      (1)

      ► 

      23 июн

      (1)

      ► 

      21 июн

      (1)

      ► 

      17 июн

      (1)

      ► 

      11 июн

      (1)

    • ► 

      мая

      (4)

      ► 

      26 мая

      (1)

      ► 

      15 мая

      (1)

      ► 

      13 мая

      (1)

      ► 

      12 мая

      (1)

    • ► 

      апреля

      (9)

      ► 

      26 апр

      (1)

      ► 

      23 апр

      (1)

      ► 

      15 апр

      (3)

      ► 

      08 апр

      (1)

      ► 

      07 апр

      (2)

      ► 

      01 апр

      (1)

    • ► 

      марта

      (5)

      ► 

      30 мар

      (1)

      ► 


      15 мар

      (1)

      ► 

      09 мар

      (1)

      ► 

      08 мар

      (1)

      ► 

      05 мар

      (1)

    • ► 

      февраля

      (3)

      ► 

      22 фев

      (1)

      ► 

      18 фев

      (1)

      ► 

      17 фев

      (1)

    • ► 

      января

      (12)

      ► 

      27 янв

      (2)

      ► 

      24 янв

      (1)

      ► 

      18 янв

      (1)

      ► 

      14 янв

      (1)

      ► 

      06 янв

      (1)

      ► 

      05 янв

      (1)

      ► 

      04 янв

      (2)

      ► 

      02 янв

      (3)

  • ► 

    2009

    (8)

    • ► 

      декабря

      (3)

      ► 

      24 дек

      (1)

      ► 

      18 дек

      (1)

      ► 

      09 дек

      (1)

    • ► 

      ноября

      (1)

      ► 

      01 ноя

      (1)

    • ► 

      октября

      (1)

      ► 

      12 окт

      (1)

    • ► 

      июля

      (1)

      ► 

      29 июл

      (1)

    • ► 

      июня

      (1)

      ► 

      19 июн

      (1)

    • ► 

      апреля

      (1)

      ► 

      29 апр

      (1)

  • ► 

    2008

    (6)

    • ► 

      апреля

      (1)

      ► 

      26 апр

      (1)

    • ► 

      марта

      (1)

      ► 

      16 мар

      (1)

    • ► 

      февраля

      (2)

      ► 

      27 фев

      (1)

      ► 

      23 фев

      (1)

    • ► 

      января

      (2)

      ► 

      15 янв

      (1)

      ► 

      13 янв

      (1)

  • ► 

    2007

    (9)

    • ► 

      декабря

      (3)

      ► 

      18 дек

      (1)

      ► 

      17 дек

      (1)

      ► 

      05 дек

      (1)

    • ► 

      ноября

      (2)

      ► 

      29 ноя

      (1)

      ► 

      28 ноя

      (1)

    • ► 

      июля

      (4)

      ► 

      27 июл

      (1)

      ► 

      21 июл

      (1)

      ► 

      20 июл

      (1)

      ► 

      19 июл

      (1)

Comparison with the 2004 proposal

Quite a few ideas in this proposal came from the 2004 proposal:

  • Conversion to string and back for places too large.
  • The performance optimizations for log10 and pow(10)
  • The FPU control word manipulation.

But there are four main differences between the 2004 proposal and this one:

Patching printf

The 2004 proposal also patches printf() in order to make rounding consistent. However, as I already explained, I don’t think that changing printf()s behaviour is such a good idea since printf behaves a certain way in every other programming language that supports that function.

A note in the manual that the precision specifier in printf() is not suitable for rounding the values should be sufficient.

No pre-rounding to precision

The 2004 proposal does not pre-round after the multiplication and thus rounding 1.255 to 2 places with the 2004 code will not work correctly either.

Additional ini settings

The 2004 proposal also adds additional ini settings for rounding mode etc. In my eyes this is superfluous since the rounding mode can always be set as an additional parameter to the round() function.

Altering floating point arithmetics in PHP core

The 2004 proposal goes way beyond traditional rounding by altering FP arithmetics in PHP in such a way that after any operation (add, subtract, multiply, divide) the result is rounded to the 15 digits precision guaranteed by IEEE 754. This destroys traditional floating point semantics but allows simple decimal calculations to work as expected by users not familiar with floating point values. It is essentially the same thing some spreadsheet applications (e.g. Microsoft Excel) do.

I’m opposed to this kind of change of general semantics since PHP never said it implemented a decimal type and if people are using it wrong, it’s their problem, we shouldn’t break other legitimate applications for this.

The round() function is a slightly different case though, since the round() function itself claims to be able to round to decimal precision. For this reason, changing round()s behaviour in order to accomodate decimal semantics is OK, since users will expect it to work that way.

Nevertheless, what could be discussed separately is the introduction of a new type that automatically uses an arbitrary precision library internally, since writing $a * $b is much more natural than e.g. bcmul($a, $b). This, however, goes far beyond the scope of this proposal.

Другие примеры округления

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

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

$x = 123.8313; echo ceil($x * 100) / 100; // 123.84

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

Так же можно поступить, если нужно округлить до меньшего значения с заданной точностью:

$x = 123.8393; echo floor($x * 100) / 100; // 123.83

Округление чисел проводится согласно определенным способам округления.

Например, существует математическое округление, округление в большую сторону, меньшую сторону, до десятков, банковское . способов как округлить числов достаточно много.

Форматирование чисел (денег) в PHP

Для удобного восприятия числа или денежные велечины, лучше выводить с разеделением тысячных групп. Например 9864 понятнее смотрится так: 9 864. Для такого форматирования в PHP есть готовые фукнции:

number_format( $number, $decimals = 0 )
number_format( $number, $decimals = 0, $dec_point = ‘.’ , $thousands_sep = ‘,’ )

Форматирует число с разделением групп.

  • number(обязательный) Число, которое нужно отформатировать.

  • decimals Сколько знаков после запятой показывать.

  • dec_point Разделитель для дробной части.

  • thousands_sep Разделитель тысяч.
// английский формат (по умолчанию)
echo number_format( 5555 ); //> 5,555

// русский формат
echo number_format( 5555, 0, ',', ' '); //> 5 555

// французский формат
echo number_format( 5555.55, 2, ',', ' '); //> 5 555,55

Для форматирования с учетом языка сайта в WordPress есть специальная функция number_format_i18n()

Round() в Go 1.10

Для тех, кто не знаком с устройством float (я в их числе), этот код выглядит совершенно непонятно. Попробуем разобраться, что же он делает:

Похоже, что мы берём битовое представление числа, сдвигаем его и применяем маску. Согласно :

Рассматривая приведённые выше константы, мы видим, что сдвиг составляет 64 — 11 — 1, что означает 64 бита на число, 11 из которых используются для показателя степени, один — для знака и 52 оставшихся бита — для мантиссы. Это означает, что используемый сдвиг удаляет биты мантиссы, а маска удаляет бит знака, оставляя нас только с показателем степени.

В полученном числе показатель степени записан не как он есть, а с прибавлением числа 1023 (это делается для того чтобы записывать отрицательные показатели для очень маленьких чисел), что означает, что мы должны вычесть 1023 из e, вычисленного выше, чтобы получить фактический показатель. Иными словами, если e < bias, то мы имеем отрицательный показатель степени, что означает, что абсолютное значение float должно быть < 1. Действительно, дальше мы видим:

Здесь бит маскируется знаковым битом, это используется только для сохранения правильного знака: теперь мы можем полностью игнорировать мантиссу. Мы можем это сделать, потому что в этом случае нас интересует только показатель степени. Так как используется основание степени 2, а e < bias, мы знаем, что наименьший показатель, который может быть, равен -1, а 2 ^ -1 = 0,5. Кроме того, мантисса имеет некоторое значение 1.X. Таким образом, в зависимости от показателя наше число находится либо в диапазоне (0,5, 1), либо в диапазоне (0, 0,5). Поэтому во втором случае для правильного округления нам нужно добавить к числу единицу. Фух. Подробнее это описано в википедии.

Теперь разберём второй случай:

Наверное, вы думаете, что условие в этой ветке должно быть e > bias, чтобы покрыть все случаи с положительным показателем степени. Но вместо этого тут используется только их часть. Использование сдвига здесь особенно интересно, потому что кажется, что оно несравнимо с bias. Первое — это число битов смещения, а второе — численное смещение. Но, поскольку числа с плавающей точкой представлены как (1.мантисса) * 2 ^ X, то если X больше числа битов в мантиссе, мы гарантированно получим значение без дробной части. То есть показатель степени сместил десятичную точку вправо настолько, что мантисса окончательно пропала. Таким образом, выражение в этой ветке игнорирует числа с плавающей точкой, которые уже округлены.

Первая строка тут простая: вычитаем bias из e и получаем реальное значение показателя степени. Вторая строка добавляет к значению 0,5. Это работает, потому что старший бит мантиссы добавляет 0,5 к финальной сумме (см. представление в статье “Википедии” ниже). В этом случае эта сумма переполняет 52-битные границы мантиссы, показатель степени будет увеличен на 1. Значение показателя степени не сможет переполниться до знакового бита, так как оно не может быть больше bias+shift из примера выше. В любом случае, дробная часть очищается. Таким образом, если дробная часть была больше или равна 0,5, она будет увеличена на 1, в противном случае будет отброшена. Хитро и не очевидно до тех пор, пока мы не посмотрим глубже.

Ошибки округления и модуль decimal

При округлении функцией round(), можно получить следующее:

round(2.65, 1) # = 2.6
round(2.85, 1) # = 2.9

Почему в одном случае округляется вниз, а в другом вверх? При переводе 2.85 в двоичную систему получается число, которое немного больше. Поэтому функция видит не «5», а «>5» и округляет вверх.

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

print (0.1 + 0.1 + 0.1)

0.30000000000000004

Из-за подобных ошибок числа типа «float» нельзя использовать там, где изменения значения на одну тысячную может привести к неверному результату. Решить данную проблему поможет модуль decimal.

decimal — модуль, позволяющий округлять десятичные дроби с почти 100% точностью. Его основной принцип: компьютер должен считать так, как считает человек. Речь идёт не о скорости вычисления, а о точности и отсутствии проблем неправильного представления чисел.

Округление до целого числа

Все функции в качестве основного параметра используют значение с плавающей точкой. Например:

При выполнении каждой функции, получим следующие значения:

echo ceil($x); // 124 (к большему целому значению) echo floor($x); // 123 (к меньшему целому значению) echo round($x); // 123 (математически к ближайшему)

При математическом округлении, учитывается значение числа округляемой части. Если оно меньше 5, округляется к меньшему. Если равно или больше 5, округление будет выполнено в большую сторону. В данном случае функция round округляет к меньшему значению, т.к. значение дробной части меньше 0.5. Если использовать значение больше, например:

В результате округления, получим:

echo round($x); // 124

Примеры

Пример: корректировка округления десятичных дробей

// Замыкание
(function() {
  /**
   * Корректировка округления десятичных дробей.
   *
   * @param {String}  type  Тип корректировки.
   * @param {Number}  value Число.
   * @param {Integer} exp   Показатель степени (десятичный логарифм основания корректировки).
   * @returns {Number} Скорректированное значение.
   */
  function decimalAdjust(type, value, exp) {
    // Если степень не определена, либо равна нулю...
    if (typeof exp === 'undefined' || +exp === 0) {
      return Math(value);
    }
    value = +value;
    exp = +exp;
    // Если значение не является числом, либо степень не является целым числом...
    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
      return NaN;
    }
    // Сдвиг разрядов
    value = value.toString().split('e');
    value = Math(+(value + 'e' + (value ? (+value - exp) : -exp)));
    // Обратный сдвиг
    value = value.toString().split('e');
    return +(value + 'e' + (value ? (+value + exp) : exp));
  }

  // Десятичное округление к ближайшему
  if (!Math.round10) {
    Math.round10 = function(value, exp) {
      return decimalAdjust('round', value, exp);
    };
  }
  // Десятичное округление вниз
  if (!Math.floor10) {
    Math.floor10 = function(value, exp) {
      return decimalAdjust('floor', value, exp);
    };
  }
  // Десятичное округление вверх
  if (!Math.ceil10) {
    Math.ceil10 = function(value, exp) {
      return decimalAdjust('ceil', value, exp);
    };
  }
})();

// Округление к ближайшему
Math.round10(55.55, -1);   // 55.6
Math.round10(55.549, -1);  // 55.5
Math.round10(55, 1);       // 60
Math.round10(54.9, 1);     // 50
Math.round10(-55.55, -1);  // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1);      // -50
Math.round10(-55.1, 1);    // -60
// Округление вниз
Math.floor10(55.59, -1);   // 55.5
Math.floor10(59, 1);       // 50
Math.floor10(-55.51, -1);  // -55.6
Math.floor10(-51, 1);      // -60
// Округление вверх
Math.ceil10(55.51, -1);    // 55.6
Math.ceil10(51, 1);        // 60
Math.ceil10(-55.59, -1);   // -55.5
Math.ceil10(-59, 1);       // -50

С этим читают