導入
Go言語でプログラミングをしていると、float32型やfloat64型を使って計算を行う場面がよくあります。しかし、浮動小数点数同士を比較する際に「==」演算子を使ってしまうと、意図しないバグに悩まされることがあります。なぜなら、コンピュータにとって浮動小数点数は「厳密な値」ではなく「近似値」として扱われるからです。今回は、この浮動小数点数の比較における落とし穴と、現場で使われる正しい解決策を解説します。
基礎知識
まず、なぜ浮動小数点数の比較が難しいのかを理解しましょう。コンピュータは数字を2進数で表現しますが、0.1のような十進数の小数は、2進数では「無限小数」になってしまいます。そのため、メモリ上では特定の桁数で打ち切る必要があり、ごくわずかな「誤差」が生じます。
これが「仮数部の微細な差異」です。例えば、計算の結果が「0.30000000000000004」になった場合、本来期待している「0.3」とはビット単位で見ると異なっているため、「==」で比較すると「false(等しくない)」と判定されてしまうのです。
実装/解決策
浮動小数点数を比較する際は、直接「==」を使うのではなく、「2つの数値の差が、許容できる非常に小さな値(イプシロン)以下であるか」を確認する方法をとります。
具体的には、|a – b| < ε(イプシロン)という式を使います。このεには、計算内容に応じて十分小さい値(例: 0.000000001など)を設定します。これにより、実用上の誤差を許容した比較が可能になります。
サンプルプログラム
以下のコードをコピーして実行し、直接比較と誤差による比較の違いを確認してみてください。
package main
import (
“fmt”
“math”
)
func main() {
a := 0.1 + 0.2
b := 0.3
// 直接比較(これはfalseになり、意図通りにいかないことが多い)
fmt.Printf(“直接比較 (a == b): %v\n”, a == b)
// 許容誤差(イプシロン)を設定
const epsilon = 1e-9
// 差の絶対値を計算して比較する
// math.Absで差を求め、それがイプシロンより小さいかを確認する
isEqual := math.Abs(a-b) < epsilon
fmt.Printf("誤差による比較 (差が %v 以下): %v\n", epsilon, isEqual)
}
応用・注意点
現場での開発における注意点を2つお伝えします。
1つ目は、イプシロンの選定です。扱う数値の大きさが極端に小さい場合や大きい場合は、適宜イプシロンの値も調整する必要があります。固定値で運用する場合は、定数として管理し、プロジェクト内で統一することが大切です。
2つ目は、金融系の計算にはfloatを使わないという鉄則です。通貨や正確な金額を扱う場合は、浮動小数点数による誤差が許されないため、整数型で管理する(例:円を銭単位で扱う)か、専用のDecimalライブラリを使用するのが一般的です。
浮動小数点数の比較は、一見単純ですが非常に奥が深いテーマです。まずはこの「誤差の範囲で判定する」という考え方をぜひ身につけてください。

コメント