Страницы

Поиск по вопросам

воскресенье, 5 января 2020 г.

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

#mysql #sql


Выполняю запрос к БД 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.
    


Ответы

Ответ 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() вообще не предусматривают приведения числа к неточному числовому типу.

Комментариев нет:

Отправить комментарий