Задавайте вопросы, мы ответим
Вы не зашли.
Страниц: 1
Всегда знал, что 'число' и число - разные вещи, но за всё время своей работы с MySQL ни разу не бывал в ситуации, когда эта разница играет роль. И вот недавно такое случилось, о чем хочу рассказать.
Если вкратце: разница может сыграть роль, когда такие величины участвуют в арифметических операциях умножения или деления.
В моём случае нужно было рассчитать цену на товар со скидкой 55%. Что, как известно, делается по простой формуле:
цена со скидкой = ROUND( цена без скидки * (1 - скидка/100) )
Случайная проверка выявила расхождение цены из базы данных и цены, посчитанной вручную на калькуляторе. При разборе формулы на части выяснилась неожиданная вещь:
mysql> SELECT 55/100, 1 - 55/100, '55'/100, 1 - '55'/100; +--------+------------+----------+---------------------+ | 55/100 | 1 - 55/100 | '55'/100 | 1 - '55'/100 | +--------+------------+----------+---------------------+ | 0.5500 | 0.4500 | 0.55 | 0.44999999999999996 | +--------+------------+----------+---------------------+
Как я понял, операция с числами дала результат типа DECIMAL - точное число, а операция с числами и строкой - FLOAT, число с плавающей точкой. Из-за различий в механизме их хранения возникло расхождение фактических величин.
На практике это привело к двум разным ценам:
29990 * 0.4500 = 13495.5000; после округления - 13496 29990 * 0.44999999999999996 = 13495.499999999998; после округления - 13495
Дополнительная сложность здесь еще и в том, что различие проявляется не всегда, а только при определенных величинах, иногда в весьма узком их диапазоне, отчего такой случай трудно бывает даже заметить.
Если подобная величина отправляется в делитель, то и здесь возникают различия:
mysql> SELECT 1/55, 1/'55'; +--------+---------------------+ | 1/55 | 1/'55' | +--------+---------------------+ | 0.0182 | 0.01818181818181818 | +--------+---------------------+
Вот такая история.
Неактивен
Спасибо! Интересный эффект. Действительно, явное преобразование во FLOAT дает такой же результат.
mysql> select 1-CAST(55 AS FLOAT)/100.; +--------------------------+ | 1-CAST(55 AS FLOAT)/100. | +--------------------------+ | 0.44999999999999996 | +--------------------------+
Неактивен
В некой базе данных имеется таблица "Пример":
+---------+--------+
| Число1 | Число2 |
+---------+--------+
| 9891.88 | 4723 |
+---------+--------+
Полям "Число1" и "Число2" назначен тип поля FLOAT.
Запрос на перемножение полей возвращает следующий результат вычисления:
+-------------------+
| Число1 * Число2 |
+-------------------+
| 46719348.68652344 |
+-------------------+
А вот калькулятор показывает чуть другой результат произведения:
9891.88 * 4723 = 46 719 349,24
Вопрос - как и в каких случаях можно работать с этим "опасным" типом FLOAT? Только для хранения данных, не предусматривающего в дальнейшем математических операций с ними?
Неактивен
У double точность повыше,
mysql> create table t_8912(c1 double, c2 double); Query OK, 0 rows affected (1.30 sec) mysql> insert into t_8912 values(9891.88, 4723); Query OK, 1 row affected (0.34 sec) mysql> select c1*c2 from t_8912; +--------------------+ | c1*c2 | +--------------------+ | 46719349.239999995 | +--------------------+ 1 row in set (0.01 sec)
Неактивен
DOUBLE и занимает вдвое больше пространства.
Вопрос остаётся - когда пользоваться FLOAT безопасно? Или, например, забыть этот тип и использовать теперь только DOUBLE? )
Я полагал, что разница в этих типах только в точности числа хранения. Разница при умножении, например, должна бы быть в пределах округления - но не такая, как в моём примере...
Неактивен
Я боюсь, что это больше вопрос не про MySQL, а в принципе про хранение и работу чисел с одинарной точностью.
Неплохая статья на эту тему есть на википедии: https://en.wikipedia.org/wiki/Single-pr … int_format (русскоязычная в этом месте — плохая). В частности, из-за особенностей формата хранения точность не будет теряться на числах, у которых в десятичном представлении не больше 6 цифр.
Про то, использовать ли FLOAT или DOUBLE — вопрос плохо поставленный. И тот и другой форматы хранят лишь приблизительное значение. У double точность безусловно выше, но легко можно придумать сценарии, когда и он будет округлять. Соответственно, ответ про формат такой: если вам критичны вычисления с какой-то точностью (например, деньги), то нужно использовать DECIMAL или INT нужных размеров (подразумевая, что вы храните в копейках, например). Если вас устраивает приблизительный результат, то FLOAT или DOUBLE сгодятся (второй делает точность, разумеется, сильно выше).
Ну и заодно собрал вот такой пример, в котором видно, что даже первое число уже хранится не так, как вы ожидаете, что уж говорить о произведении:
Неактивен
Спасибо большое!
Для начала уже хорошо понимать, что оно вот так всё. )
Попробую разобраться, какую неточность можно ожидать, если придётся работать с плавающей точкой...
Неактивен
Страниц: 1