MySQL: руководство профессионала
Шрифт:
Этот раздел обсуждает точность математического округления для функции ROUND и для вставок в столбцы с типами с точным значением.
Функция ROUND работает по-разному в зависимости от того, является ли параметр точным или приблизительным числом:
Для числа точного значения ROUND использует метод "округления половины": значение с дробной частью .5 или больше округлено до следующего целого числа, если положительное, или до предыдущего целого числа, если отрицательное. Значения с дробной частью меньше, чем .5,
Для числа приблизительного значения результат зависит от библиотеки C. На многих системах это означает, что значение с любой дробной частью округлено ROUND к самому близкому четному целому числу.
Следующий пример показывает, как округление отличается для точных и приблизительных значений:
mysql> SELECT ROUND(2.5), ROUND(25E-1);
+------------+--------------+
| ROUND(2.5) | ROUND(25E-1) |
+------------+--------------+
| 3 | 2 |
+------------+--------------+
Для вставок в DECIMAL или целочисленный столбец, если адресат представляет собой точный тип данных, используется метод "округления половины" независимо от того, является ли значение, которое будет вставлено, точным или приблизительным:
mysql> CREATE TABLE t (d DECIMAL(10,0));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t VALUES(2.5),(2.5E0);
Query OK, 2 rows affected, 2 warnings (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 2
mysql> SELECT d FROM t;
+------+
| d |
+------+
| 3 |
| 3 |
+------+
1.5. Примеры математической точности
Этот раздел обеспечивает некоторые примеры, которые показывают запросы с математической точностью в MySQL 5.1.
Пример 1. Числа используются с их точным значением, как даны, когда возможно:
mysql> SELECT .1 + .2 = .3;
+--------------+
| .1 + .2 = .3 |
+--------------+
| 1 |
+--------------+
Для значений с плавающей запятой, результаты неточны:
mysql> SELECT .1E0 + .2E0 = .3E0;
+--------------------+
| .1E0 + .2E0 = .3E0 |
+--------------------+
| 0 |
+--------------------+
Другой способ увидеть различие в точной и приблизительной обработке значения состоит в том, чтобы добавить маленькое число к сумме много раз. Рассмотрите следующую сохраненную процедуру, которая добавляет .0001 к переменной 1000 раз:
CREATE PROCEDURE p
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE d DECIMAL(10,4) DEFAULT 0;
DECLARE f FLOAT DEFAULT 0;
WHILE i < 10000 DO
SET d = d + .0001;
SET f = f + .0001E0;
SET i = i + 1;
END WHILE;
SELECT d, f;
END;
Сумма для d и f логически должна быть 1, но это истинно только для десятичного вычисления. Вычисление с плавающей запятой представляет маленькие ошибки:
+--------+------------------+
| d | f |
+--------+------------------+
| 1.0000 | 0.99999999999991 |
+--------+------------------+
Пример 2. Умножение выполняется с масштабом, требуемым стандартом SQL. То есть, для двух чисел X1 и X2, которые имеют масштаб S1 и S2, масштаб результата: S1+S2:
mysql> SELECT .01 * .01;
+-----------+
| .01 * .01 |
+-----------+
| 0.0001 |
+-----------+
Пример 3. Поведение округления четко:
Поведение округления (например, с функцией ROUND) независимо от реализации основной библиотеки C, что означает, что результаты непротиворечивы на разных платформах.
Округление для столбцов с точным значением использует округление половины, как показано здесь:
mysql> SELECT ROUND(2.5), ROUND(-2.5);
+------------+-------------+
| ROUND(2.5) | ROUND(-2.5) |
+------------+-------------+
| 3 | -3 |
+------------+-------------+
Однако, округление для значений с плавающей запятой использует библиотеку C, которая на многих системах использует другую логику работы:
mysql> SELECT ROUND(2.5E0), ROUND(-2.5E0);
+--------------+---------------+
| ROUND(2.5E0) | ROUND(-2.5E0) |
+--------------+---------------+
| 2 | -2 |
+--------------+---------------+
Пример 4. В строгом режиме вставка значения, которое является слишком большим, приводит к переполнению и ошибке, а не к усечению до допустимого значения. Когда MySQL не выполняется в строгом режиме, происходит усечение к допустимому значению:
mysql> SET sql_mode='';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);