Db2のOracle互換機能を使ってみた😃<9>　～NUMBERタイプの互換性(3)～

今回ご紹介したいのは、数値の丸めに関してです。

#■OracleのNUMBERとDb2のNUMBERの異なる点
##その２：数値の丸め
OracleのNUMBERの数値の丸めは四捨五入されるというのが基本仕様のようです。
一方Db2ではDECIMAL型やINTEGERなどの整数型で丸めが必要になった場合は、切捨てが基本です。
Db2にはデータベース構成パラメータにDECFLT_ROUNDINGというパラメータがあり、丸めのルールを設定できるのですが、この指定が有効なのはNative Db2ではDECFLOATのデータ・タイプだけであり、DECIMALやINTEGERなどには効かないため、データ挿入時に四捨五入で丸めることはできません。

ただOracle互換のDb2においては、このDECFLT_ROUNDINGの設定がNUMBERに対して有効、つまりDECIMALにも有効になります。

###DECFLT_ROUNDINGの設定
DECFLT_ROUNDINGに設定できるルールは5種類があり、その中のROUND_HALF_UPのルールが四捨五入に相当するのですが、デフォルトはROUND_HALF_EVENになっています。
ということは、Oracle互換のDb2のNUMBERは内部的にDECIMALであるため、DECFLT_ROUNDINGの設定をデフォルトのままにしておくと、四捨五入でなく、ROUND_HALF_EVENのルールで丸めが生じるということになります。

**ROUND_HALF_EVEN**
このROUND_HALF_EVENというルールはあまり馴染みがないですが、IEEE の 10 進浮動小数点仕様で定められているデフォルトの丸めモードということで、Db2はこれをデフォルトとしています。
四捨五入では切り捨てられる数字が5の場合は切り上げになりますが、5の左にある数字が常に偶数になるように切り捨てか切り上げかが変わるというのがROUND_HALF_EVENです。
切り捨てられる5の左にある数字というのは、例えば135を10の位で丸める場合の3の部分の数字のことです。

ROUND_HALF_EVENの丸めの具体例を見てみましょう。
Oracle互換のDb2にNUMBER(4,1)のNUMB_col列と、確認のためDECIMAL(4,1)のDEC_col列を定義した表を作成します。
また挿入した値をそのまま表示できるようINPUT_NUM列も作成しておきます。

```
create table numbtab_4_1
(CASE      char(1),
 NUMB_col  NUMBER(4,1),
 DEC_col   DECIMAL(4,1),
 INPUT_NUM DECFLOAT(16));
```
この表に6つの数字を挿入します。caseがA~Cは小数点以下第1位が4、D~Fは5になる数字とし、小数点以下第2位はそれぞれ1,5,9の3パターンにしています。

```
insert into numbtab_4_1 values
('A',123.41,123.41,123.41),
('B',123.45,123.45,123.45),
('C',123.49,123.49,123.49),
('D',234.51,123.51,123.51),
('E',234.55,123.55,123.55),
('F',234.59,123.59,123.59);
```
この場合、NUMB_colもDEC_colも位取りが1であるのに対して、挿入値は小数点以下2位まであるため、INSERT時に値の丸めが生じます。

INSERT結果が以下になります。注目はBとEの行です。
**ROUND_HALF_EVENの丸めの具体例**
![丸め1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528658/ff0eda31-be1f-d094-73a1-19ed16cc03ce.png)
BとEの行は廃棄される数字が5ですが、その左側の数字がBは4の偶数なので、そのまま切り上げられずに4になります。
一方Eは5の左の数字が奇数の5なので、一番近い偶数に切り上げられ、6になります。
つまりOracle互換のDb2のNUMBERは、**そのままでは四捨五入のルールは適用されない**ということです。

**四捨五入はROUND_HALF_UP**
丸めのルールを四捨五入にしたい場合は、DECFLT_ROUNDINGの設定をROUND_HALF_UPに変更する必要があります。
変更は、以下のようにデータベース構成パラメータを変更します。

**DECFLT_ROUNDINGの変更**
![DBCFG変更.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528658/b5ee8576-37a1-7a16-48f2-1e73a40e8c73.png)
設定は動的には反映しないので、一度セッションを終了し再接続し、同じデータを入れなおした結果が以下です。

**DECFLT_ROUNDINGの設定をROUND_HALF_UPに変更した後の結果**
![丸め2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528658/02e4ac0e-f15b-5dcc-1342-53884e12ac1b.png)
Bの行のNUMB_col列もDEC_col列も四捨五入で123.5になりました。

ちなみにDECFLT_ROUNDING=ROUND_HALF_UP の**NativeのDb2**のDECIMAL(4,1)の列に上記と同じ値を挿入した結果は以下です。
四捨五入の設定ですが、データ・タイプがDECIMALなので、NativeのDb2ではすべて切り捨てになっています。

**DECFLT_ROUNDING=ROUND_HALF_UPのNative Db2の場合の結果**
![丸め_NativeDb2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528658/0ce44302-4c4a-6e3e-4772-453f5c4341f1.png)

###整数データ・タイプの利用時には注意
[前回の～NUMBERタイプの互換性(2)～][8] の小数点の有無の違いの紹介で、整数であることが分かっているならNUMBERでなく2進整数のデータ・タイプを使用することを検討してみてください、と言いました。
しかし、もし入力データに小数点以下がある可能性があり、整数値への丸めをNUMBERの四捨五入の丸めルールに任せていた場合は注意が必要です。

というのは、上記で説明したようにOracle互換のDb2で四捨五入の丸めができるのはDECIMALかDECFLOATのみで、INTEGERなどの整数型は切り捨てルールが適用されることに変更はありません。
例えばNUMBER(4)に10.5というデータを挿入して、それをOracleの四捨五入にまませて11にさせていた場合、これをINTEGER で定義すると、10になってしまいます。

そのためデータ・タイプを整数型にするなら、入力値には小数点以下がないことを確実にするか、小数点以下がありOracleと同じように四捨五入した数で格納したいのであれば、DECFLT_ROUNDINGを ROUND_HALF_UPに設定した上で、位取りが0のNUMBER（またはDECIMAL）にする必要があります。

この丸めの結果の違いは、アプリケーションテストの段階になってから、影響が判明することも多いので、事前に把握しておくことは重要かと思います。

###参考資料のご紹介
DECFLT_ROUNDINGの詳細は以下をご参照ください。
（参考資料A：decflt_rounding - 10 進浮動小数点丸め構成パラメーター）
https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_10.5.0/com.ibm.db2.luw.admin.config.doc/doc/r0052298.html

またOracle互換のNUMBERデータ・タイプとdecflt_roundingに関しては、以下にも記載されています。
（参考資料B: NUMBER データ・タイプ）
https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.1.0/com.ibm.db2.luw.apdv.porting.doc/doc/r0052879.html

次回は、NUMBERとDECIMALの3つ目の違いとして、精度と位取りの指定できる値の違いによる考慮事項について触れたいと思います。

つづく

##関連投稿
[・Db2のOracle互換機能を使ってみた😃<1>　～データベースの作成～][1]
[1]:https://qiita.com/Seven_Marine/items/1a9009a29e78fc80a2f0
[・Db2のOracle互換機能を使ってみた😃<2>　～可変長列の末尾ブランクの違い～][2] 
[2]:https://qiita.com/Seven_Marine/items/f7a31fc71a728282e043
[・Db2のOracle互換機能を使ってみた😃<3>　～空文字(長さ0の文字)の扱い(1)～][3]
[3]:https://qiita.com/Seven_Marine/items/470d58226fe89f017b2a
[・Db2のOracle互換機能を使ってみた😃<4>　～空文字(長さ0の文字)の扱い(2)～][4] 
[4]:https://qiita.com/Seven_Marine/items/612e1ca67054337f9758
[・Db2のOracle互換機能を使ってみた😃<5>　～空文字(長さ0の文字)の扱い(3)～][5] 
[5]:https://qiita.com/Seven_Marine/items/28c73c824619baf48354
[・Db2のOracle互換機能を使ってみた😃<6>　～空文字(長さ0の文字)の扱い(4)～][6] 
[6]:https://qiita.com/Seven_Marine/items/5cc1afad26ea4eea4644
[・Db2のOracle互換機能を使ってみた😃<7>　～NUMBERタイプの互換性(1)～][7] 
[7]:https://qiita.com/Seven_Marine/items/74bf4af0e65de2222e33
[・Db2のOracle互換機能を使ってみた😃<8>　～NUMBERタイプの互換性(2)～][8] 
[8]:https://qiita.com/Seven_Marine/items/a9b2c0e8a03695ef82c1
[・Db2のOracle互換機能を使ってみた😃<10>　～NUMBERタイプの互換性(4)～][10] 
[10]:https://qiita.com/Seven_Marine/items/b260192afc9ec5c60899

-----
お断り：
当投稿は、Database migration to DB2-IBM Japan Community Wikiに掲載していたブログの未公開部分を、Qiitaに投稿するものです。

本資料掲載事項は、ある特定の環境・使用状況においての正確性は確認されていますが、すべての環境において同様の結果が得られる保証はありません。
これらの技術を自身の環境に適用する際には、自己の責任において十分な検証と確認を実施いただくことをお奨めいたします。