そういえば、ふと昔のことを思い出したので書いてみます。
2014年の8月頃、私が受託開発の下請けをやっていた時の話です。
💡 【連載:開発の記憶】
本記事は、受託開発の現場で直面したトラブルや葛藤を振り返る全3回の連載です。
- 第1回:DBをFLOAT型にしても誤差は出るというお話(現在の記事)
- 第2回:システムが出力したExcelが開けない!?犯人は「メモ帳」だった話
- 第3回:「セキュリティは不要」と指示されたエンジニアの葛藤
当時のプロジェクトは、発注元が画面設計やDB設計を行い、私はその仕様に基づいてプログラムの実装のみを担当するという流れでした。
その中で、設計担当の方のデータ型に対する理解不足から生じた「誤差」について、なぜか実装側の私に疑いの目が向けられるという、少し理不尽なやり取りがありました。
「プログラムが勝手に丸めている」疑惑
無事に実装を終えて検証フェーズに入った頃、発注元の担当者さんから「計算結果が合わない」というメールが届きました。
入力画面の合計値がExcelでの計算結果と異なり、「小数部が勝手に繰り上がって計算されているのではないか」とのことでした。
画面設計書を確認すると、項目の書式は整数表記を前提としたものでしたが、念のため「小数表示が必要な仕様でしたか?」と返信したところ、次のような追撃が来ました。
○説明不足ですみません。
調査した結果、小数部の丸めではなく整数部自体に誤差が生じていると判断しました。
Excelは小数で計算されていますが、プログラム側で勝手に整数に丸めて計算しているように感じます。
いわゆる「プログラムのバグ」扱いですね。
原因はプログラムではなく「FLOAT型」
今回の私の実装は、Excelの数式結果を取得してそのままDBに格納し、画面表示時に再取得するという非常にシンプルなものでした。プログラム内で独自の計算や丸め処理は一切行っていません。
そこで提供されたDBのテーブル定義を改めて確認してみたところ、原因が見えてきました。
合計値を格納するカラムのデータ型が、「FLOAT型(単精度浮動小数点型)」で定義されていたのです。
ご存知の方も多いと思いますが、FLOAT型は大きな桁数を扱うと整数部であっても丸め誤差が発生する特性があります。今回のデータには5億を超えるような大きな数値が含まれており、完全にFLOAT型の精度限界を超えていました。
「FLOAT型の罠」をそっとお伝えする
私は、あまり嫌味にならないよう、あくまで技術的な事実を検証データとともに丁寧に返信しました。
プログラム側では計算を行わず、取得した値をそのままDBに格納・表示しています。
DBの型がFLOAT型であるため、そこで誤差が発生している可能性が高いです。試しに、管理ツール等から直接
553275881.9という数値を手入力してみてください。
おそらく、勝手に553275904のような値に変わってしまうはずです。桁数が大きくなれば整数部でも誤差が発生するのが浮動小数点型ですので、設計時に意図した型選択であったかご確認いただけますでしょうか。
気まずい沈黙と、その結末
このメールを送信した後、数日間連絡が途絶えました。おそらく手元の環境で手入力を試し、「本当に数字が変わる……」と絶望されていたのだと思います。
数日後、ようやく届いたメールにはこう書かれていました。
FLOATで誤差が出る件、私が思っている以上に誤差が出ていました。
申し訳ありませんが、FLOATをDECIMAL型へ変更したいと思いますので対応をお願いします。
「思っている以上に」という言い回しに、設計ミスを認めざるを得なかった担当者さんの苦渋が滲んでいるようで、思わずクスッとしてしまいました。
まとめ:厳密な数値にはDECIMAL型を
教訓は非常にシンプルです。
「金額や合計値など、1円(1単位)の狂いも許されない数値にFLOAT型を使ってはいけない」
設計書で合計値のカラムがFLOAT型になっているのを見かけたら、実装前にそっと型変更を提案してあげるのが、後々のトラブルを防ぐ優しさなのかもしれませんね。