Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pautov_MySQL_rukovodstvo_professionala.328368.rtf
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
713.1 Кб
Скачать

1.4. Поведение округления

Этот раздел обсуждает точность математического округления для функции 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);

Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO t SET i = 128;

Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> SELECT i FROM t;

+‑‑‑‑‑‑+

| i |

+‑‑‑‑‑‑+

| 127 |

+‑‑‑‑‑‑+

1 row in set (0.00 sec)

Однако, условие переполнения происходит, если включен строгий режим:

mysql> SET sql_mode='STRICT_ALL_TABLES';

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE t (i TINYINT);

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t SET i = 128;

ERROR 1264 (22003): Out of range value adjusted for column 'i' at row 1

mysql> SELECT i FROM t;

Empty set (0.00 sec)

Пример 5 : В строгом режиме и с настройкой ERROR_FOR_DIVISION_BY_ZERO деление на нуль вызывает ошибку, а не результат NULL.

В нестрогом режиме деление на нуль имеет результат NULL:

mysql> SET sql_mode='';

Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE t (i TINYINT);

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t SET i = 1 / 0;

Query OK, 1 row affected (0.00 sec)

mysql> SELECT i FROM t;

+‑‑‑‑‑‑+

| i |

+‑‑‑‑‑‑+

| NULL |

+‑‑‑‑‑‑+

1 row in set (0.03 sec)

Однако, деление на нуль выдает ошибку, если соответствующие SQL‑режимы активны:

mysql> SET sql_mode='STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO';

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE t (i TINYINT);

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t SET i = 1 / 0;

ERROR 1365 (22012): Division by 0

mysql> SELECT i FROM t;

Empty set (0.01 sec)

Пример 6 . До MySQL 5.0.3 литералы с точным значением и с приблизительным значением преобразованы в значения с плавающей запятой двойной точности:

mysql> SELECT VERSION();

+‑‑‑‑‑‑‑‑‑‑‑‑+

| VERSION() |

+‑‑‑‑‑‑‑‑‑‑‑‑+

| 4.1.18‑log |

+‑‑‑‑‑‑‑‑‑‑‑‑+

1 row in set (0.01 sec)

mysql> CREATE TABLE t SELECT 2.5 AS a, 25E‑1 AS b;

Query OK, 1 row affected (0.07 sec)

Records: 1 Duplicates: 0 Warnings: 0

mysql> DESCRIBE t;

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| Field | Type | Null | Key | Default | Extra |

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| a | double(3,1) | | | 0.0 | |

| b | double | | | 0 | |

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

2 rows in set (0.04 sec)

Начиная с MySQL 5.0.3, литерал с приблизительным значением все еще преобразован в значение с плавающей запятой, но литерал с точным значением обработан как DECIMAL:

mysql> SELECT VERSION();

+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+

| VERSION() |

+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+

| 5.1.6‑alpha‑log |

+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+

1 row in set (0.11 sec)

mysql> CREATE TABLE t SELECT 2.5 AS a, 25E‑1 AS b;

Query OK, 1 row affected (0.01 sec)

Records: 1 Duplicates: 0 Warnings: 0

mysql> DESCRIBE t;

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| Field | Type | Null | Key | Default | Extra |

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| a | decimal(2,1) unsigned | NO | | 0.0 | |

| b | double | NO | | 0 | |

+‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

2 rows in set (0.01 sec)

Пример 7 . Если параметр функции точный числовой тип, результат также точный числовой тип, с масштабом по крайней мере, как у параметра. Рассмотрите эти инструкции:

mysql> CREATE TABLE t (i INT, d DECIMAL, f FLOAT);

mysql> INSERT INTO t VALUES(1,1,1);

mysql> CREATE TABLE y SELECT AVG(i), AVG(d), AVG(f) FROM t;

Результаты до MySQL 5.0.3:

mysql> DESCRIBE y;

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| Field | Type | Null | Key | Default | Extra |

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| AVG(i) | double(17,4) | YES | | NULL | |

| AVG(d) | double(17,4) | YES | | NULL | |

| AVG(f) | double | YES | | NULL | |

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

Результат двойной точности, независимо от типа параметра. А вот результаты в MySQL 5.0.3 и выше:

mysql> DESCRIBE y;

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| Field | Type | Null | Key | Default | Extra |

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

| AVG(i) | decimal(14,4) | YES | | NULL | |

| AVG(d) | decimal(14,4) | YES | | NULL | |

| AVG(f) | double | YES | | NULL | |

+‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑+‑‑‑‑‑+‑‑‑‑‑‑‑‑‑+‑‑‑‑‑‑‑+

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]