要点
- Python・JavaScriptでは、今回の実測で足し算の結果と
0.3が一致しなかった(出典: 自社の実測・2026-10-25)。 - Goでは、数値を直接書くか、変数に入れてから足すかによって比較結果が変わった(出典: 自社の実測・2026-10-25)。
- Goの公式仕様は、定数式をコンパイル時に評価すると定めている(出典: Go言語仕様・Constant expressions)。
何が困るのか:同じ足し算でも比較結果が違う
0.1 + 0.2の結果は0.3と等しいのか。Python・JavaScriptでは、今回の実測で足し算の結果と0.3が一致しなかった。Goでは、数値を直接書くか、変数に入れてから足すかによって比較結果が変わった。
測定日は2026-10-25。記録された環境は、Python 3.9.0、Node.js v22.23.4、Goのバージョン出力がgo version go1.26.2 darwin/arm64というものだ。Python・Node.jsについて、個別のOS情報は記録されていない。
Pythonで記録された入力と出力は次のとおりだった。
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
JavaScriptでも、足し算の表示は0.30000000000000004で、=== 0.3の結果はfalseだった。
> 0.1 + 0.2
0.30000000000000004
> 0.1 + 0.2 === 0.3
false
一方、Goでa := 0.1 + 0.2と書いた場合は、a == 0.3がtrueになった。
a := 0.1 + 0.2
fmt.Println(a) // 0.3
fmt.Println(a == 0.3) // true
ここで確かめたいのは、比較までにどのような計算をしているかだ。Goで一致したという結果だけでは、変数を使ったコードでも一致するとは判断できない。実際に変数を経由すると、同じGoでも比較結果はfalseになった。
出典: 自社の実測(2026-10-25)
公式はどう書いているか:定数式はコンパイル時に評価する
Goの定数式とは、定数のオペランドだけを含み、コンパイル時に評価される式のことだ。
今回の0.1 + 0.2は、この定数式に当たる。Goの公式仕様には次の文がある。
Constant expressions may contain only constant operands and are evaluated at compile time.
出典: Go言語仕様・Constant expressions(取得日 2026-10-25)
また、同仕様では浮動小数点の定数について、少なくとも256ビットの仮数部で表現することが示されている。今回の0.1 + 0.2は、型の付いていない定数どうしの式だ。変数に格納した浮動小数点数の足し算と、同じ経路で計算されるわけではない。
Pythonの公式チュートリアルには、10進の0.1を2進の分数として正確に表せないという説明がある。この制約と、Goの定数式をいつ評価するかという規定は、分けて読む必要がある。Goで比較が真になったことは、float64が10進の小数をすべて正確に表せるという意味ではない。
出典: Python公式チュートリアル・浮動小数点演算、その問題と限界(取得日 2026-10-25)
今回のGoの定数式では、高精度のまま足してから、最後にfloat64へ丸める。その結果が、比較対象の0.3をfloat64で表した値と一致した。一方、float64変数を経由する場合には、それぞれの値を丸めた後で足し算する。この順序の違いが、記録された比較結果を読む際のポイントになる。
Python・JavaScriptの今回の計算でも、0.1と0.2はそれぞれ先に浮動小数点数へ丸められる。丸め誤差を含んだ値を足すため、その結果は0.3を浮動小数点数にした値とは異なる。
比較表:言語だけでなくGoの書き方も分ける
実測結果を並べると、Goをひとまとめにするだけでは違いを説明できないことが分かる。次の表では、数値を直接書く場合と、変数を経由する場合を別の行にした。表示欄は記録された表示方法によるもので、すべてを同じ書式で出力した比較ではない。
| 対象 | 計算の書き方 | 記録された表示 | 0.3との比較 |
|---|---|---|---|
| Python | 0.1 + 0.2 |
0.30000000000000004 |
False |
| JavaScript | 0.1 + 0.2 |
0.30000000000000004 |
false |
| Go・定数式 | a := 0.1 + 0.2 |
fmt.Println(a)で0.3 |
true |
| Go・変数経由 | var x, y float64 = 0.1, 0.2の後にa := x + y |
%.17fで0.30000000000000004 |
false |
Goで両方の経路を同じ書式で表示した記録もある。
var x, y float64 = 0.1, 0.2
a := x + y
fmt.Printf("%.17f\n", a) // 0.30000000000000004
fmt.Println(a == 0.3) // false
b := 0.1 + 0.2 // 両方ともリテラル(定数式)
fmt.Printf("%.17f\n", b) // 0.29999999999999999
fmt.Println(b == 0.3) // true
定数式の側も、この書式では0.29999999999999999と表示されている。それでも比較はtrueだ。これは、定数式の結果と比較対象の0.3が、同じfloat64の値に丸められたためである。
一方、変数を経由した側は0.30000000000000004と表示され、比較結果もfalseだった。0.1と0.2をそれぞれ変数に入れた時点で丸めが発生し、その後で足し算している。定数式の結果だけを見て、変数を使う処理も同じ結果になると考えることはできない。
この例では、表示を0.3に近づけることと、比較する値が一致することを区別できる。fmt.Println(b)の表示は0.3だが、同じbを小数点以下17桁で表示すると0.29999999999999999になる。表示する桁数が変わっても、記録されたb == 0.3の結果はtrueである。
出典: 自社の実測(2026-10-25)
どちらを使うか:丸めて比較した結果と適用範囲
今回の記録には、Python・JavaScriptで丸めてから比較した例もある。Pythonではround(0.1 + 0.2, 10) == 0.3がTrue、JavaScriptではNumber((0.1 + 0.2).toFixed(10)) === 0.3がtrueだった。
>>> round(0.1 + 0.2, 10) == 0.3
True
> Number((0.1 + 0.2).toFixed(10)) === 0.3
true
この結果から確認できるのは、記録にある値と丸め方の組み合わせでは比較が一致した、という範囲だ。丸める桁数を変えた場合や、別の値を入力した場合まで、同じ結果が保証されたわけではない。
また、0.1 + 0.1 + 0.1を0.3と比較した例でも、Python・JavaScriptでは偽、Goの定数式では真となった。
>>> 0.1 + 0.1 + 0.1 == 0.3
False
> 0.1 + 0.1 + 0.1 === 0.3
false
c := 0.1 + 0.1 + 0.1
fmt.Println(c == 0.3) // true
Goでは、この式も定数式として計算され、最後に丸めた値が0.3の浮動小数点表現と一致した。ただし、Goの定数式が常に人の直感と一致するかどうかは、全探索されていない。今回確認したのは、記録にある2組の足し算である。
コードを確かめる際には、実際の処理が定数式なのか、変数を使う計算なのかをそろえる必要がある。変数を使う処理の確認を、数値を直接書いたGoの式だけで済ませると、今回のような差を見落とすことになる。
金額や比率のように厳密な一致が必要な値では、浮動小数点数の==による比較を判断の前提にしない。今回の丸め方で一致したことと、あらゆる入力で厳密な一致を扱えることは、別に考える必要がある。
出典: 自社の実測(2026-10-25)
自分の環境で確かめる手順
次は読者が実行するためのコマンドであり、追加の実測結果ではない。Python・JavaScriptの比較に続いて、Goでは定数式と変数経由の式を比較する。printfの行は、作業ディレクトリにfloat-check.goを作成するため、同名のファイルがない場所で実行する。
python3 -c 'print(0.1 + 0.2); print(0.1 + 0.2 == 0.3)'
node -e 'console.log(0.1 + 0.2); console.log(0.1 + 0.2 === 0.3)'
printf '%s\n' 'package main' 'import "fmt"' 'func main() { var x, y float64 = 0.1, 0.2; a := x + y; b := 0.1 + 0.2; fmt.Println(a == 0.3); fmt.Println(b == 0.3) }' > float-check.go
go run float-check.go
確認するときは、比較結果とともに、言語や実行環境のバージョン、数値を直接書いたか変数を経由したかを残したい。表示だけを見て判断せず、等価比較そのものの結果も記録すると、今回の表と対応させやすい。
Goの確認用コードでは、先に変数経由のa == 0.3、次に定数式から代入したb == 0.3を表示する。素材の実測に対応する結果は、順にfalse、trueだ。どちらも同じ0.1と0.2の足し算だが、足す前にfloat64変数へ入れるかどうかが異なる。
バージョンは、今回の記録でも使われた次のコマンドで確認できる。
python3 --version
node --version
go version
なお、元の記録では途中計算の中間精度や処理速度は測定していない。Go以外の言語における定数式の扱いも、今回の確認範囲には含まれない。この記事で扱う違いは、記録された式と環境における比較結果、および公式文書に示された規定に限られる。

コメント