2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SQLアンチパターン ラウンディングエラー

Last updated at Posted at 2020-08-27

ラウンディングエラー

floatやdoubleのような実数を表すデータ型を使用する際、小数値の値を使用すると丸めが発生して正確な数が格納できない。
floatは実数を2進数形式でエンコードする。

10進数で記述できる全ての値を、2進数で格納できるわけではない。

丸めが避けられない

3分の1のような有理数は0.3333…のような循環小数で表す場合、正確な値は少数では表現できない。
桁数とは数の精度であり、循環小数では無限精度が必要になる。
 
妥協策は可能な限りオリジナルに近い値、例えば0.333などの有限精度の値を使用すること。
しかしこの妥協策は丸めの振る舞いによる誤差を生み出します。

1/3 + 1/3 + 1/3 = 1
0.333 + 0.333 + 0.333 = 0.999

3分の1の近似値である0.333の精度を上げてから3倍しても真値である1.0にはならない。
これが循環小数を持つ数を表すために止むを得ず有限精度を使用するという妥協策である。

0.333333 + 0.333333 + 0.333333 = 0.999999 ←有限精度を使うと1.0にはならない

SQLでのFLOATの使用

SQLのFLOATデータ型は、IEEE754標準にしたがって実数を2進数形式でエンコードする。
IEEE754では、浮動小数点数を2進数形式で表現する。

2進数形式で無限精度が必要な値は、10進数で表現される値とは異なる。
10進数では有限精度で表せる値として59.95を例として説明する。

FLOAT型では無限精度を扱うことができないため2進数形式で格納できる近似値を使用する。
その近似値を10進数で表現すると「59.950000762939」となる。

この小さな誤差が.....

浮動小数点の誤差累積の影響は和ではなく積を計算する場合にさらに大きくなる。
元々の誤差は小さく見えるが計算によってみるみる大きくなる。

とある値Aがあったとする。

値Aに1.0を何回掛けようと値Aの結果は変わらない。
値Aにかける値が0.999だった場合は誤差が大きくなっていく。

値Aに0.999を1000回かけると誤差は約0.3677となる。
計算回数がさらに増えるとさらに誤差が大きくなる。

一つ値にかけ算を何回も繰り返し行う典型的な例は金融システムにおける利子の複利計算などがある。
初めは小さな誤差だが計算を繰り返すうちに誤差はどんどん大きくなる。

金融系に限った話ではないが正確な値を使うことは重要である。

アンチパターンの見つけ方

FLOAT、REAL、DOUBLE、PRECISIONなどのデータ型をもちいると「完全に正確な値」は期待できない可能性が高い。
そのため上記のデータ型を用いている場合、アンチパターンを用いている可能性が高いと言える。

解決策

NUMERIC型、DECIMAL型を使用する。

上記のデータ型では有理数を丸めこむことなく格納できるためその値を正確に格納され信頼できる値となる。
しかし上記の型を用いても3分の1のような無限精度が必要な値を格納することができない。

正確な値が必要な場合はNUMERIC型、DECIMAL型を使用。
FLOAT型など丸めこみが発生する型の場合は概数として扱うべきである。

まとめ

できる限り、FLOAT型は使わないようにしましょう。

文字ばかりの記事ですみません.....

次回

サーティワンフレーバー

参考文献

SQLアンチパターン

参考文献.jpg

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?