面接官「0.1 + 0.2 == 0.3 が成立しない理由を説明せよ」
という質問がきたらどう答えるだろうか。
0.3にはならない
Javascript
適当なブラウザから以下のようなJavascript実行した。結果「0.1+0.2==0.3」は「false」となった。
0.1+0.2==0.3
//false
0.1+0.2===0.3
//false
ちなみに以下のような先頭に0をつけなくても、小数点として解釈される。
.1+.2==.3
//false
Python
バージョン3.10.11で検証する。同様に「0.1+0.2==0.3」は「false」となった。
>>> 0.1+0.2==0.3
False
なぜか
これは、機械語が2進数へ変換される際に、浮動小数点数の誤差が発生するためです。小数点以下の数値は多くの場合、無限小数となります。
小数点以下の数値→2進数へ変換する方法
CSを修めていればご存知方と思いますが、復習します。
- 小数点以下の数値を2倍する
- 結果の整数部分を取り出し、それを2進数の桁として記録する
- 小数部分だけを次のステップに持ち越す
- 小数部分が0になるか、所定の精度に達するまで1〜3を繰り返す
0.1 × 2 = 0.2
整数部分: 0
小数部分: 0.2
0.2 × 2 = 0.4
整数部分: 0
小数部分: 0.4
0.4 × 2 = 0.8
整数部分: 0
小数部分: 0.8
0.8 × 2 = 1.6
整数部分: 1
小数部分: 0.6
(以下省略)
最終的に0.1は0.0001100110011001100110011....
となる。
まとめ
この問題の本質は、コンピュータが浮動小数点数を扱う際の誤差にあります。浮動小数点数の精度と表現方法を理解しておくことが重要です。
(追記)
言語仕様によるよ!
どういった仕様で実装されているか本質的な部分にも関心向ける機会ってあんまりなですよね。
コメント
double だとそうですが、float だと誤差が収まってしまう為 true になるんですよね
具体例
playground
※ const と var 両方でやっているのは const は コンパイル段階で計算してしまう為比較用
@juner 動的言語は人類には過ぎた技術だったようです。
@mlab 動的言語だからというよりも IEEE 754 の倍精度 64ビットバイナリー形式 (CやC#でいう double) だからですね。
32ビット浮動小数点(CやC# でいう float) も 今回のケースでは運がよかっただけです。
「0.1 + 0.2 == 0.3 が成立しないかは言語にもよる話ですがこの質問は前提条件が足りないと思います」
@fujitanozomu
成立しない前提で、何を問われているか理解するの難しいですよね。わかります。
@mlabさん、
0.1 + 0.2 == 0.3 が成立しないかは言語にもよる話なので「実行する言語を間違えた」という回答は自分的にはアリだと思います。
あるいは「使用したコンピュータのソフトかハードに異常があった」という回答も可能でしょう。
このような回答は、0.1 + 0.2 == 0.3 が成立しない前提でこの質問を出してる面接官的にはどうなのだろうかと思いました。
「(JavaScript なら)実行可能なので成立します!」
そもそも浮動小数を等号で比較するのはよくありません。例えば、pythonであれば、math.isclose() を使って比較しましょう。また、roundなどで丸めてから比較するのもよいでしょう。
浮動小数ではなく、分数で表現するなどの工夫が必要かもしれません。
@mlabさん、
機械語は関係ないですね。
IEEE 754のbinary64で
0.1
等が正確に表せられないことは「0.1 + 0.2 == 0.3が成立しない」理由のひとつではありますが、0.1 + 0.1 == 0.2
や0.1 + 0.3 == 0.4
が成立する例を見ると0.1 + 0.2 == 0.3
が成立しないことの理由はもう少し踏み込んだ説明が必要と言えますね。いいね以上の気持ちはコメントで