【C++学習|実務向け】C++実務の罠:浮動小数点の比較はなぜ「==」を使ってはいけないのか

導入

C++で開発を行っている際、浮動小数点型(floatやdouble)を扱うことは避けて通れません。しかし、多くのエンジニアが「==」演算子を使用して浮動小数点同士を比較し、バグを埋め込んでしまいます。なぜなら、浮動小数点はコンピュータ内部で二進数として表現される際、多くの値で正確に表現できず、微小な「誤差」が生じるからです。本記事では、この誤差を正しく扱い、現場で安全な比較を行うための手法を解説します。

基礎知識

浮動小数点数は、IEEE 754という規格に基づいて格納されています。例えば、0.1という数値は、二進数では循環小数となり、有限のビット数では完全に表現できません。そのため、計算過程で0.1を足し合わせると、0.3になるはずが「0.30000000000000004」のようになることがあります。この状態で「==」を使用して比較を行うと、数学的には等しいはずの数値が「異なる」と判定され、if文の分岐が想定外の挙動を引き起こす原因となります。

実装/解決策

浮動小数点の比較において、実務上の定石は「二つの値の差が、十分に小さい値(イプシロン:ε)以下であるか」を判定することです。具体的には、std::absを用いて差の絶対値を求め、あらかじめ定義しておいた許容誤差と比較します。この「許容誤差」は、アプリケーションの精度要件に応じて調整する必要があります。

サンプルプログラム

以下のコードは、浮動小数点の比較を安全に行うための基本的な実装例です。

include
include // std::absを使用するために必要

int main() {
double a = 0.1 + 0.1 + 0.1;
double b = 0.3;

// 誤った比較方法
if (a == b) {
std::cout << "== での比較:等しい" << std::endl; } else { std::cout << "== での比較:異なる" << std::endl; } // 正しい比較方法:許容誤差(イプシロン)を定義する // 1e-9は一般的な精度の目安だが、計算内容に応じて調整が必要 const double epsilon = 1e-9; if (std::abs(a - b) < epsilon) { std::cout << "許容誤差を用いた比較:等しい" << std::endl; } else { std::cout << "許容誤差を用いた比較:異なる" << std::endl; } return 0; }

応用・注意点

実務でこの手法を用いる際は、以下の点に注意してください。

1. 許容誤差の選定
計算結果が非常に大きな値になる場合や、逆に極めて小さな値になる場合は、固定の「1e-9」では不十分なことがあります。そのような場合は、値の大きさに応じて許容誤差を相対的に変化させる(相対誤差を用いる)手法も検討してください。

2. C++20の活用
C++20からは、標準ライブラリに浮動小数点の比較をサポートする機能が追加されています。std::numeric_limits::epsilon() を利用して、型に応じた最小の差分を取得する習慣をつけるのも良いアプローチです。

3. 厳密な比較が必要な場合
もし金融計算など、絶対に誤差が許されないシステムを開発している場合は、浮動小数点型自体を避けるべきです。代わりに、整数型で管理する(例:100円を10000銭として管理する)か、多倍長演算ライブラリの使用を強く推奨します。

コメント

タイトルとURLをコピーしました