小数型を扱うときは気にしたほうがいい
面積を扱う機会があり、「mysql 小数」でググって出てきた記事をうのみにして実装したところ、以下のような現象が発生しました。
CREATE TABLE DATA (ID INT, MENSEKI FLOAT);
INSERT INTO DATA (ID, MENSEKI) VALUES (1, 2.1);
SELECT COUNT(*) FROM DATA WHERE MENSEKI >= 2.1;
--1件該当
SELECT COUNT(*) FROM DATA WHERE MENSEKI >= 2.1 AND MENSEKI <= 2.1;
--NOT FOUND !?
SELECT COUNT(*) FROM DATA WHERE MENSEKI <= 2.1;
--NOT FOUND !?
ただし、あらゆるレコードで発生するのではなく、小数点以下がゼロでないレコードでこの現象が発生します。
これは、以下のSQLを実行するとわかります。
SELECT MENSEKI + 0 FROM DATA;
--2.0999999046325684
要は、INSERTされた値は、SELECTで与えているリテラル値と違うんですね。
これは、FLOAT型が IEEE754単精度浮動小数点数 で格納されているためです。
解決策
- DECIMAL(10,2) 型を利用したら、期待通り動作した
- FLOAT(10,2) と桁を指定したら期待通り動作した
- DOUBLE を利用したら期待通り動作した
追記
@reta さんコメントありがとうございます。
基本的に理由がなければ DECIMAL を利用すべきとご意見いただきました。
面積程度の値なら、浮動小数点を使う必要ないということだと思いました。
まとめ
構築時にユースケースになくても、こういう単体テストはちゃんとやったほうがいい。
やらないと「バグ」って言われます・・・・