MySQL 8.0 リファレンスマニュアル


12.25.5 高精度計算の例

このセクションでは、MySQL での精度の高い数学的クエリー結果を示す例をいくつか示します。 これらの例は、セクション12.25.3「式の処理」およびセクション12.25.4「丸め動作」で説明されている原則を示しています。

例 1 数値は、可能であれば、指定されたとおりにその厳密値で使用されます。

mysql> SELECT (.1 + .2) = .3;
+----------------+
| (.1 + .2) = .3 |
+----------------+
|              1 |
+----------------+

浮動小数点値の場合、結果は不正確です。

mysql> SELECT (.1E0 + .2E0) = .3E0;
+----------------------+
| (.1E0 + .2E0) = .3E0 |
+----------------------+
|                    0 |
+----------------------+

厳密値と近似値の処理の違いを確認するための別の方法として、合計値に小さい数値を何回も加える方法があります。 ある変数に .0001 を 1,000 回加える次のストアドプロシージャーを考えてみます。

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;

論理的には、df の両方の合計値が 1 になるはずですが、それは 10 進数の計算にしか当てはまりません。 浮動小数点の計算では、小さなエラーが発生します。

+--------+------------------+
| d      | f                |
+--------+------------------+
| 1.0000 | 0.99999999999991 |
+--------+------------------+

例 2 乗算は、標準 SQL に必要なスケールで実行されます。 つまり、スケール S1S2 を持つ 2 つの数値 X1X2 の場合、結果のスケールは S1 + S2 になります。

mysql> SELECT .01 * .01;
+-----------+
| .01 * .01 |
+-----------+
| 0.0001    |
+-----------+

例 3 厳密値数値に対する丸め動作は、適切に定義されています。

丸め動作 (たとえば、ROUND() 関数を使用した動作) は、ベースとなる C ライブラリの実装には依存しません。つまり、その結果は各プラットフォームにわたって一貫性があります。

  • 厳密値カラム (DECIMAL と整数) および厳密値数値に対する丸めでは、四捨五入ルールが使用されます。 小数部が .5 以上の値は、次に示すようにゼロから最も近い整数に丸められます:

    mysql> SELECT ROUND(2.5), ROUND(-2.5);
    +------------+-------------+
    | ROUND(2.5) | ROUND(-2.5) |
    +------------+-------------+
    | 3          | -3          |
    +------------+-------------+
  • 浮動小数点値に対する丸めでは、C ライブラリが使用されます。これは、多くのシステムでは偶数丸めルールを使用します。 2 つの整数の間に小数部が完全に半分の値がある場合、最も近い偶数に丸められます:

    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 が設定されている場合は、0 による除算によって NULL の結果ではなく、エラーが発生します。

非厳密モードでは、0 による除算によって 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 モードが有効になっている場合、0 による除算はエラーになります。

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 厳密値リテラルは、厳密値として評価されます。

近似値リテラルは浮動小数点を使用して評価されますが、正確な値リテラルは DECIMAL として処理されます:

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> 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    |       |
+--------+---------------+------+-----+---------+-------+

結果が倍精度値になるのは浮動小数点引数の場合だけです。 厳密値型引数の場合は、結果も厳密値型になります。


関連キーワード:  関数, 計算, 精度, 空間, Query, リファレンス, 数値, ROUND, rows, CREATE