プログラムにはRubyを使用する

桁落ち

1
2
3
4
printf("%.20g\n",1.1-1.0) 
# 0.10000_00000_00000_08882
p (1.1-1.0).class
# Float

1.1-1.0を10進数で考えると、0.1であるが、コンピュータは2進数で扱っている(データを保持している)ので、2進数で考える必要がある。0.1を2進数に直すと、0.00011_00110_011...(0011が続く)となる。また、上記プログラムより、(1.1-1.0)はFloat型である。ここで、Rubyのドキュメントによると、RubyのFloat型はC言語のdouble型であるとわかる。IEEE754より、倍精度浮動小数点数の仮数部は52ビットであるので、先の無限小数を52ビットで丸める必要がある。小数点以下52桁までで最近接偶数方向丸めをおこなうと、仮数部は.00011_00110_01100_11001_10011_00110_01100_11001_10011_00110_10になる。これを10進数に直すと、.10000_00000_00000_08881_7となり、小数点以下20桁で表すと、0.10000_00000_00000_08882になるので、プログラムの実行結果と一致する。

情報落ち

1
2
printf("%.20g\n",1.0+0.00000_00000_00000_9)
# 1.00000_00000_00000_8882

1.00000_00000_00000_9を2進数で表すと、1.00000_00000_00000_00000_00000_00000_00000_00000_00000_00001_00000...(無限小数)となる。これも問題aと同様に52ビットに丸める必要があるので、丸めると仮数部は.00000_00000_00000_00000_00000_00000_00000_00000_00000_00001_00となる。10進数で表すと1.00000_00000_00000_88817となり、これを小数点以下20桁で表すと、1.00000_00000_00000_8882になる。よってプログラムの実行結果と合致する。

丸め誤差

1
2
3
4
5
6
printf("%.20g %.20g\n",7*0.1,7.0/10)
# 0.70000_00000_00000_06661 0.69999_99999_99999_95559
printf("%.20g %.20g\n",7*0.125,7.0/8)
# 0.875 0.875
printf("%.20g %.20g\n",7*0.0625,7.0/16)
# 0.4375 0.4375

プログラムの実行結果から掛ける数が2の累乗の場合に乗算と除算の結果が一致していることがわかる。これは割ったり掛けたりする数を2進数に直したときに小数点以下52ビット以内の有限小数となるため、誤差が生じない。よって、乗除算の結果が一致する。ただし、計算途中で丸められた場合はその限りではない。