【C言語】floatとdoubleで計算したら計算結果が異なる!誤差が…!の前置き〈進数その2〉
元気いっぱい,今日も頑張って記事を書くぞ~!と意気込んでいるあこさんです.
さて,前回の続きです.
4,10進数をN進数に変換
昨日の逆です.
これ,やり方を知らないと結構厄介です.
仮に18という10進数の数があったとします.
これを2,8,16進数へと変換しようと思います.
まず解答から.
2進数=10010(2)
8進数=22(8)
16進数=12(16)
となりますね.
さて,ここから本題.計算の仕方.
「18」という変換したい数に対してN(進数の数)で割ります.
で,整数の商となるように計算して余りを出していきます.
余りを最後に出た方から読めば変換されたものになります.
というわけで実践.
18/2=9...0
9/2=4...1
4/2=2...0
2/2=1...0
1/2=0...1
下から上に余りを読んで10010.なっていますね.
商が0になるまでやりましょう.
次,8進数.
18/8=2...2
2/8=0...2
下から22.合ってる.
次16進数
18/16=1...2
1/2=0...1
下から12.合ってる.
と,こんな感じに計算をすると変換が簡単にできます.
5,2進数から8進数
ここからは豆知識.
2進数って,一けたに対し2種類の数字を使えることから2の乗数個の表現が可能です.
そして8進数の場合は1桁で8種類の数字を使える.これは2の3乗.
つまり,2進数の3桁=8進数の1桁となります.
ので,1の位から3桁ずつ切っていきそれぞれの区間で10進数に一度直す.それを8進数に直して並べれば変換ができる!のです!
100111という2進数があったとして,100 | 111と分けられます.
それぞれ,4|7であることから返還後は47となるとわかりますね.
6,2進数から16進数
同様に16=2^4であることから2進数4桁=16進数1桁である.
ので,1101010こういう数列に対してまず区切る.
110 | 1010となる.のでそれぞれを10進数に直す.
6 | 10となる.これをそれぞれ16進数に対応する数に直す.
よって,6Aとなる.
7,実は一体一対応ではない.
これが本題です.
感の良い方はもうその1の時には気づいていたかもしれませんが10進数で表せる数と2進数で表せる数は1対1対応ではありません.
というのも小数点問題です.
1/2...1/4...1/8でしか2進数は小数点以下を表現できません.
桁が少なければ少ないほど雑になりますね….
0.1という数を2進数に変換すると…
0000 0000 . 0001 1001 1001 1001 1001 1001 ........ となります.
このあとずっと1001が続いていきます.
でこれを再度10進数に戻すと…0.099999964237213134765625と,0.1に戻りません.
8,PCによる誤差の発生
そして結論です.
コンピュータ内はすべて2進数で表されています.
よって,上のような数値を入れたとき一度2進数に変換されます.
で10進数に再変換して戻ってきたときには…
となります.
これが誤差の正体です.
そして,これは桁数が少なければ少ないほど大きい誤差となります.
コンピューターで変数に0.1を代入し,あとからその変数ないの数字が0.1であるかみたいなものを聞くと0.1ではないと答えることがあります.
この現象もすべてこの「誤差」によるものなのです.
いやぁ,いやですよね.1対1に写像してくれない像なんて.
というわけで進数変換について前置きは理解できたでしょうか….
最後の結論さえ理解出来れいれば問題ありません.
次回,次々回あたりでfloatとdoubleの話を書きますのでもしよければお付き合いくださいませ.