Почему округление до сотых при помощи ROUND() в MySQL округляет вниз?

182
24 марта 2019, 10:00

Выполняю запрос к БД MySQL, чтобы получить проценты за платеж:

select cp.id,
    cp.percent,
    round(cp.percent, 2)
from payment cp
where cp.id = 4118098
    and month(cp.payment_date) = 9

Выдаёт:

Вопрос: почему округление до сотых округляет вниз, хотя на конце цифра 5?

При этом запрос select round(75.725, 2) округляет всё корректно - до 75.73.
Тип столбца percent в таблице как double.

Answer 1

почему округление до сотых округляет вниз, хотя на конце цифра 5?

Тип столбца percent в таблице как double.

Тип DOUBLE относится к т.н. "неточным" типам данных - т.е. используемое для его хранения двоичное представление не в точности равно введённому, а лишь максимально близко к нему. Как итог - печально известные 0,1000000001 или 0,0999999999 вместо введённого изначально 0,1. И false при сравнении такого значения с непосредственно заданным в виде литерала значением 0,1.

В соответствии с документацией для таких типов данных

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 a fractional part exactly halfway between two integers is rounded to the nearest even integer.

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

Для того, чтобы получить округление "половина - вверх", необходимо привести сначала имеющееся значение к "точному" числовому типу, т.е. желаемое округление можно было бы получить так:

SELECT ROUND(CAST(cp.percent AS DECIMAL(6,3)), 2)

Однако приведение типа само по себе требует округления (помним, что исходное значение-то у нас неточное!), а потому есть смысл сразу выполнять приведение к нужному конечному типу, т.е. с нужным количеством десятичных знаков. И окончательный вариант будет выглядеть как

SELECT CAST(cp.percent AS DECIMAL(5,2))

При этом запрос select round(75.725, 2) округляет всё корректно - до 75.73.

В данном случае константа-литерал сразу создаётся с типом DECIMAL. Потому и округление работает, как указано в той же документации:

For exact-value numbers, ROUND() uses the “round half away from zero” 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.

или, если по-русски, "округление к ближайшему целому в направлении от нуля", т.е. положительные - вверх, а отрицательные - вниз.

PS. Обратите внимание - функции преобразования типа CAST() и CONVERT() вообще не предусматривают приведения числа к неточному числовому типу.

READ ALSO
Как скрыть background кнопки

Как скрыть background кнопки

Только в хроме наблюдается проблема с отображением кнопки - полоски вертикальныеПомогите их убрать

262
Картинки в один ряд по горизонтали

Картинки в один ряд по горизонтали

Изображения одинакового размера, но не становятся в один ряд по горизонтали

164
Перенос нескольких блоков сразу (Flex)

Перенос нескольких блоков сразу (Flex)

Имеется условный флексовый div, в нём 4 элементаПри уменьшении разрешения экрана, соответственно и уменьшении div'а, необходимо что бы сдвигались...

150