精密計算
TiDBの高精度演算サポートはMySQLと一致しています。詳細については、 MySQL における精密計算参照してください。
数値型
正確値演算における高精度演算の範囲には、正確値データ型(整数型およびDECIMAL型)と正確値数値リテラルが含まれます。近似値データ型と数値リテラルは浮動小数点数として扱われます。
正確な値の数値リテラルは、整数部、小数部、またはその両方を持ちます。符号付きでも構いません。例: 1
、 .2
、 3.4
、 -5
、 -6.78
、 +9.10
。
近似値の数値リテラルは-1.2E-3
仮数部と指数部を用いた科学的記数法(10の累乗)で表されます。仮数部と指数部のいずれか、または両方に符号-1.2E3
付けることができます。例: 1.2E3
1.2E-3
似ているように見える2つの数値でも、扱いが異なる場合があります。例えば、 2.34
正確な値(固定小数点)ですが、 2.34E0
近似値(浮動小数点)です。
DECIMAL データ型は固定小数点型であり、計算は正確です。FLOAT および DOUBLE データ型は浮動小数点型であり、計算は概算です。
DECIMALデータ型の特性
このセクションでは、DECIMAL データ型 (およびその同義語) の特性に関する次のトピックについて説明します。
- 最大桁数
- 保存形式
- ストレージ要件
DECIMAL列の宣言構文はDECIMAL(M,D)
です。引数の値の範囲は次のとおりです。
- M は最大桁数 (精度) です。1<= M <= 65。
- D は小数点の右側の桁数 (スケール) です。1 <= D <= 30 であり、D は M 以下でなければなりません。
Mの最大値65は、DECIMAL値の計算精度が65桁までであることを意味します。この65桁の精度制限は、正確な値の数値リテラルにも適用されます。
DECIMAL 列の値は、9桁の小数点を4バイトにパックするバイナリ形式で保存されます。各値の整数部と小数部のstorage要件は別々に決定されます。9桁の倍数ごとに4バイトが必要で、残りの桁には4バイトの何分の1かのバイトが必要です。残りの桁に必要なstorage要件は、次の表に示されています。
残った数字 | バイト数 |
---|---|
0 | 0 |
1~2 | 1 |
3~4 | 2 |
5~6 | 3 |
7~9 | 4 |
例えば、 DECIMAL(18,9)
列は小数点の両側に9桁ずつあるため、整数部と小数部はそれぞれ4バイト必要です。3 DECIMAL(20,6)
列は14桁の整数部と6桁の小数部で構成されます。整数部は9桁で4バイト、残りの5桁で3バイト必要です。小数部は6桁で3バイト必要です。
DECIMAL列には、先頭の+
文字目、 -
文字目、または先頭の0
桁目は格納されません。9 DECIMAL(5,1)
列に+0003.1
挿入した場合、 3.1
として格納されます。負の数の場合、リテラルの-
文字目は格納されません。
DECIMAL列では、列定義で指定された範囲を超える値は許可されません。例えば、 DECIMAL(3,0)
列は-999
から999
までの範囲をサポートします。7 DECIMAL(M,D)
列では、小数点の左側に最大M - D
桁までしか許可されません。
DECIMAL 値の内部形式の詳細については、TiDB ソース コードのmydecimal.go
参照してください。
式の処理
精密計算を含む式の場合、TiDB は可能な限り、指定されたとおりの正確な数値を使用します。例えば、比較における数値は、値を変更せずに指定されたとおりに使用されます。厳密な SQL モードでは、正確なデータ型を列に追加すると、その列の範囲内であれば、その数値が正確な値で挿入されます。取得される値は、挿入された値と同じです。厳密な SQL モードが有効になっていない場合、TiDB では INSERT の切り捨てが許可されます。
数値式の処理方法は、式の値によって異なります。
- 式に近似値が含まれている場合、結果は近似値になります。TiDB は浮動小数点演算を使用して式を評価します。
- 式に近似値が含まれず、正確な値のみが含まれる場合、および正確な値に小数部分が含まれる場合、式は DECIMAL の正確な算術を使用して評価され、精度は 65 桁になります。
- それ以外の場合、式には整数値のみが含まれます。式は正確です。TiDBは整数演算を使用して式を評価し、BIGINT(64ビット)と同じ精度を持ちます。
数値式に文字列が含まれている場合、文字列は倍精度浮動小数点値に変換され、式の結果は近似値になります。
数値列への挿入はSQLモードの影響を受けます。以下の説明では、strictモードとERROR_FOR_DIVISION_BY_ZERO
について説明しています。すべての制限を有効にするには、strictモードの値とERROR_FOR_DIVISION_BY_ZERO
両方を含むTRADITIONAL
モードを使用します。
SET sql_mode = 'TRADITIONAL`;
数値を厳密な型(DECIMALまたは整数)の列に挿入する場合、その数値が列の範囲内であれば、その正確な値が挿入されます。この数値の場合:
- 値の小数部の桁数が多すぎる場合は、丸めが行われ、警告が生成されます。
- 値の整数部の桁数が多すぎる場合は、値が大きすぎるため、次のように処理されます。
- 厳密モードが有効になっていない場合、値は最も近い有効な値に切り捨てられ、警告が生成されます。
- 厳密モードが有効になっている場合、オーバーフロー エラーが発生します。
文字列を数値列に挿入する場合、文字列に数値以外の内容が含まれている場合、TiDB は次のように文字列から数値への変換を処理します。
- 厳密モードでは、数字で始まっていない文字列(空文字列を含む)を数字として使用することはできません。エラーまたは警告が発生します。
- 数値で始まる文字列は変換可能ですが、末尾の非数値部分は切り捨てられます。厳密モードでは、切り捨てられた部分にスペース以外の文字が含まれている場合、エラーまたは警告が発生します。
デフォルトでは、0による除算の結果はNULLとなり、警告は表示されません。SQLモードを適切に設定することで、0による除算を制限できます。SQLモードERROR_FOR_DIVISION_BY_ZERO
有効にすると、TiDBは0による除算を以下のように処理します。
- 厳密モードでは、挿入と更新は禁止され、エラーが発生します。
- 厳密モードでない場合は警告が発生します。
次の SQL ステートメントでは、
INSERT INTO t SET i = 1/0;
さまざまな SQL モードで次の結果が返されます。
sql_mode 値 | 結果 |
---|---|
'' | 警告なし、エラーなし。i は NULL に設定されます。 |
厳しい | 警告なし、エラーなし。i は NULL に設定されます。 |
ERROR_FOR_DIVISION_BY_ZERO | 警告、エラーなし。i は NULL に設定されています。 |
厳格、 ERROR_FOR_DIVISION_BY_ZERO | エラー。行は挿入されません。 |
丸め動作
ROUND()
関数の結果は、その引数が正確か近似かによって異なります。
正確な数値の場合、
ROUND()
関数は「半分を切り上げる」ルールを使用します。近似値の数値の場合、TiDB の結果は MySQL の結果と異なります。
TiDB > SELECT ROUND(2.5), ROUND(25E-1); +------------+--------------+ | ROUND(2.5) | ROUND(25E-1) | +------------+--------------+ | 3 | 3 | +------------+--------------+ 1 row in set (0.00 sec)
DECIMAL または整数列に挿入する場合、丸めにはゼロから半分を丸める使用されます。
TiDB > CREATE TABLE t (d DECIMAL(10,0));
Query OK, 0 rows affected (0.01 sec)
TiDB > INSERT INTO t VALUES(2.5),(2.5E0);
Query OK, 2 rows affected, 2 warnings (0.00 sec)
TiDB > SELECT d FROM t;
+------+
| d |
+------+
| 3 |
| 3 |
+------+
2 rows in set (0.00 sec)