☆☆10進数の実数を2進数で表現する コンピュータで計算をおこなった結果 コマンドプロンプト画面に近似値が表示される訳 をご紹介します。




☆☆10進数の実数を2進数で表現する コンピュータで計算をおこなった結果 コマンドプロンプト画面に近似値が表示される訳 をご紹介します。☆☆




マックス 「2進数111.111は10進数であらわすと


2進数111.111=


1×(2の2乗)+1×(2の1乗)+1 +


1×(2の-1乗=2分の1)+1×(2の-2乗=4分の1)+1×(2の-3乗=8分の1)=


4+2+1+0.5+0.25+0.125=10進数7.875


となる。


とりあえずの仕組みはわかった。


では・・・


どうよっ


2進数10.001なら・・・


2進数10.001=1×(2の1乗)+0×1+0×(2の-1乗=2分の1)+0×(2の-2乗=4分の1)+1×(2の-3乗=8分の1)


=2+0.125=2.125


と こうなるんだなあ(^^)」


老人 「いいところまでいったのう。


では 逆に10進数7.875を2進数であらわすには・・・」


マックス 「もう簡単 、まず10進数7の部分は2進数111。で


いいはず。


0.5=(2の-1乗=2分の1)=2進数0.1

0.25=(2の-2乗=4分の1)=2進数0.01

0.125=(2の-3乗=8分の1)=2進数0.001

0.0625=(2の-4乗=16分の1)=2進数0.0001

・・・

となっていることを使い


7ののこりの


0.875は0.5, 0.25, 0.125 ,0.0625 ・・・を


つかって 山崩しをする


まず


0.875から


0.5(2進数0.1)をとることができるので


0.875=0.5+・・・・


0.875=2進数0.1+・・・とあらわされる。


残りの0.875-0.5=0.375は


もう0.5でとることができないので


つぎは0.5の半分の0.25(2進数0.01)でとる。


すると


0.875=0.5+0.25+・・・


イコール


0.875=2進数0.1+2進数0.01・・・とあらわされる。


残りの0.875-0.5-0.25=0.125は


もう0.25でとることができないので


次は


0.25の半分の0.125でとる。


すると


10進数0.875=10進数0.5+0.25+0.125とあらわされ


これを2進数表示すると


10進数0.875=10進数0.5+0.25+0.125

2進数0.1+2進数0.01+2進数0.001


=2進数0.111とあらわされる。


よって整数部分の10進数7=2進数111とあわせると


10進数7.875=7+0.875


=111+0.111


10進数7.875=111.111


これにて完了だ。


これは・・・やっぱり・・・


 おもったより簡単か・・・?」


マックスが2進数を理解しているのを


みて満足な様子の聖霊。


マックス 「ははは なんだか教えてもらったな 2進数の聖霊よ。」


緑の芝生に朝日が差し込んでくる。


老人 「では0.33はどう表す?」


マックス 「まず2進数で山崩しをするのに0.5 (2進数0.1)では


大きすぎるから0.25(2進数0.01)で山をとっていく。


0.33=0.25+・・・・・


イコール


0.33=(2進数0.01)+・・・・・


となる


残りの0.33ー0.25=0.08は


0.125(2進数0.001)より山が小さいので


山崩しに0.125はつかうことができない。


0.0625(2進数0.0001)を山崩しに使うと



0.33=0.25+0.0625+・・・・・


イコール


0.33=(2進数0.01)+(2進数0.0001)+・・・


となる


残りの0.33-0.25-0.0625=0.0175は


0.03125(2進数0.00001)より山が小さいので


山崩しに0.03125はつかうことができない。


そこで


0.03125(2進数0.00001)を2で割った


0.015625(2進数0.000001)を山崩しに使うと



0.33=0.25+0.0625+0.015625+・・・・・


イコール


0.33=(2進数0.01)+(2進数0.0001)+(2進数0.000001)・・・


となる



少しだけ残った数、0.33-0.25-0.0625-0.015625=0.001875は


0.0078125(2進数0.0000001)

0.00390625(2進数0.00000001)

0.001953125(2進数0.000000001)

より山が小さいので


0.0078125(2進数0.0000001)

0.00390625(2進数0.00000001)

0.001953125(2進数0.000000001)

は山崩しにはつかうことができない。


そこで


0.001953125(2進数0.000000001)を2で割った


0.0009765625=(2進数0.0000000001)を0.33の山崩しに使う。


すると                              

0.33=0.25+0.0625+0.015625+0.0009765625+・・・・・


2進数表示に変換すると


0.33=(2進数0.01)+(2進数0.0001)

+(2進数0.000001)+(2進数0.0000000001)・・・


=0.0101010001+・・・


となる


???


こ、これは・・・


10進数0.33を2進数に変換するのに


きりがない???


もしかしてこれが無限小数ってやつか?


10進数0.33は2進数で表しきることができないぃ・・・


ぴくぴく・・・・・・・・


嬉びに打ち震えるマックス。


ぴくぴぴくっ・・・・・・・・


目を見開くマックス。


うおおっ~~~


0.33=0.25+・・・・・<<<<<<右辺に注目

0.33=0.25+0.0625+・・・・・=0.3125+・・・<<<一番右の右辺に注目

0.33=0.25+0.0625+0.015625=0.328125+・・・<<<一番右の右辺に注目

0.33=0.25+0.0625+0.015625+0.0009765625+・・・

=0.3291015625+・・・<<<一番右の右辺に注目


😊2進数の項を足して行く度に


無限に0.33に値が近づいていっている!!!😝


なんだ、これは


俺は・・ 誰?


いったいなんなんだあああああ・・・・


0.33に0.25や0.0625とかを足してここまで近づいていけるとは・・・



( 私も0.33を0.25や0.0625・・・などをつかって表わしていっても


だいたい0.325・・・ぐらいまでしか近似できないんだろうなあ


とおもっていました。


が、


いくらでも0.33の近くまで近似することができましたのです。(^^)


                       solarplexussより)


 ♪                ♪ 


♪                     ♪


♪       ♪       ♪     

♪                     ♪   


    ♪   ♪       ♪       ♪     



    ♪       ♪       ♪     

この世界に・・・

♪          ♪              ♪


  ♪           ♪



♪       ♪       ♪     


♪          ♪            ♪     


・・・・・いっぱいの・・・・・


♪       ♪       ♪     


♪                    ♪


 ♪        ♪        ♪


♪     ♪           ♪       


♪           ♪            ♪       


♪          ♪         ♪

   ♪      

        ♪              ♪     

ああっ2進数の歌がきこえる。


 ♪          ♪             ♪



♪             ♪           ♪



♪         ♪           ♪


♪               ♪    


なに  なに  2進数の超少女よ・・・

♪             ♪           ♪



♪         ♪           ♪


♪               ♪ 

なんだろうか・・・

♪             ♪           ♪



♪         ♪           ♪


♪               ♪ 

コンピュータは有限回で計算をやめる・・・?

♪             ♪           ♪



♪         ♪           ♪


♪               ♪ 

♪             ♪           ♪



♪         ♪           ♪


♪               ♪


以下 マックスの仮設の仮説です。


10進数0.33を2進数で置き換える際


コンピュータは無限につづく2進数

 

0.33=0.0101010001+・・・を


0.33=0.010101と近似する・・・

(ここで計算をやめるというわけです)


そうかもしれんな・・・


無限に計算しても・・・


0.33=0.01010100011010101010100111101000000110100000101110100010101010101010000001111111110000000111111000001010000001111011101101111111110000000100101010100101010100101010101000100010000010000111000001000010000001000101000000110101010101001111111000101111000001000101010100000101000100001001010100101001010100100001010101110101010100・・・・・・・


とつづいていくものな・・・


も、もしや


小数点以下の項を含む10進数数値を


かけ合わせたり割ったりなどの計算を


プログラムを組んで実行した場合


たまに


コンパイル結果に


表示される数値が


真の値でない


近似値が


でてくるのはこれが原因か・・・



つまり


コンピュータに


1×0.03を計算させたいとする。


ソースコードで


printf("%f",1*0.03);


と記述すると


コンピュータは


2進数しか理解できないので


コンパイラが


10進数同士の掛け算

1*0.03の


10進数1

10進数0.03


コンピュータにも理解できるよう


2進数に翻訳してコンピュータに渡す。


1も0.03も2進数に変換してメモリに格納してから


コンピュータ内でかけあわせるが


10進数1は2進数1になるが


0.33を2進数に変換しようとすると


無限に0.0101010001+・・・と数がつづいてしまう。


そこで


1×0.03の計算プログラムを


コンパイルすると


まずはコンパイラが


10進数1は2進数1

に変換して


10進数0.33は


   🍊0.33=0.010101のように近似された2進数数値🍊

に変換して


コンピュータのメモリに送り込む。


そして


その2進数を受け取ったコンピュータが


10進数1×0.03=

2進数1×0.010101

をおこない


10進数1×0.03=2進数1×0.010101=


2進数0.010101となる。


この2進数0.010101を再び10進数に戻すと


0.328125となる。


だから


コマンドプロンプト画面では


0.33なはずの答えが0.328125と表示される・・・


原理的には


こういうことだったのか!


2進数の超少女よ・・・


(このような現象は古いコンパイラではよくみられましたが

最近のコンパイラはうまくつくりこまれていて

近似値が表示されるのをみかけることはあまりありません)

♪             ♪           ♪



♪         ♪           ♪


♪               ♪ 

♪             ♪           ♪



♪         ♪           ♪


♪               ♪ 


🍋🍋🍋

どのように10進数数値を2進数数値に近似するかはコンパイラによって


違いがあります。


それを処理系依存とよびます。


🍉🍉🍉

ここでさらにもうすこし


コンピュータで計算をおこなった結果 


コマンドプロンプト画面に近似値が出てくる訳 を考察してみます。


#include <stdio.h>


int main(void)

{

float a,

a=1.05;


printf("あなたの預金額はただいま%f円です。\n",10000*a);

return 0;

}


プログラムの実行結果(EAZY IDECの場合)


あなたの預金額はただいま10499.999523円です。


ビルド結果(Visual Studioの場合)


あなたの預金額はただいま10500.000000円です。


このプログラムのプログラムの実行結果では


(EAZY IDECの場合)は10500.000000とならずに近似値がでていますね。


この計算では


変数aに数値1.05を代入したものに数値10000をかけあわせています。


では 次のプログラムをご覧ください


#include <stdio.h>


int main(void)

{

float a,

a=1.05;


printf("あなたの預金額はただいま%f円です。\n",10000*1.05);

return 0;

}


プログラムの実行結果(EAZY IDECの場合)


あなたの預金額はただいま10500.000000円です。


ビルド結果(Visual Studioの場合)


エラー C2086 'float a': 再定義されました。 Project6 c:\users\solarplexuss\source\repos\project6\project6\source.cpp 6

警告 C4305 '初期化中': 'double' から 'float' へ切り詰めます。 Project6 c:\users\solarplexuss\source\repos\project6\project6\source.cpp 6


と表示されますが


プログラムを実行すると


あなたの預金額はただいま10500.000000円です。


と表示されます


このプログラムでは変数aを経由せず


直接 数値1.05を10000にかけあわせました。


その結果 (EAZY IDECの場合)も


プログラムの実行結果に近似値はでてきませんでした。


この(EAZY IDECの場合)のプログラムの実行結果の違いは


変数aに数値1.05を格納するかしないか にかかわっているとおもわれます。


変数aに数値1.05を格納した場合


10進数値1.05は2進数としてコンピュータのメモリに格納されます


そのとき


10進数値1.05は2進数で表そうとしても


2進数の無限小数となるので


コンピュータのメモリに格納しきれません。


そこで


10進数値1.05は 


ある2進数の値Aで1.05に近似された状態でコンピュータのメモリに格納されます


1.05にかけあわせる


数値10000は 2進数1001110001に変換されコンピュータのメモリに格納されます


よって


2進数A(1.05に近似された数値)に


2進数1001110001(数値10000)をかけあわせたものを


%f出力変換子をつかってprintf出力表示するので


プログラムの実行結果は


あなたの預金額はただいま10500.000000円です。


でなく


あなたの預金額はただいま10499.999523円です。


となるわけです。


そして この


%f出力変換指定子をつかって


2進数A(1.05に近似された数値)に


2進数1001110001(数値10000)をかけあわせたものを


小数点以下6桁の10進数値に変換してprintf出力表示する段階でも


小数点以下7桁の10進数値に四捨五入がおこなわれてから


      あなたの預金額はただいま10499.999523円です。


が表示されています。


近似が何回かおこなわれているわけです。


手が こんでいますね。


ですので 結局のところ


実際には 


10000*(かける)1.05がおこなわれているのではなく


10000*(かける)1.0499999523のようなものがおこなわれるようなことになります


ではなぜ?


変数aを経由せず


直接 数値1.05を10000にかけあわせたプログラムでは


プログラムの実行結果に近似値がでてこなかったのでしょうか。


それは


おそらくコンパイラが


1.05*10000


は明らかに答えが10500.000000となるのを判別したからではないでしょうか?


1.05*10000は10500.000000となるように設計者がコンパイラを設定したというわけです


つまり


コマンドプロンプト画面に表示される


計算結果が10500.000000となるよう


調整したのだと思われます。


それに対し


変数aを経由する場合では


とにかく


1.05を近似した2進数数値が


変数aに格納されて


変数a×10000をなんの考えもなく


もろに実行するため


プログラムの実行結果(EAZY IDEC)の場合)は


あなたの預金額はただいま10499.999523円です。


と なったのじゃないかな😊笑?


以上 推察をくわえてみました💖。





以下 は おまけの考察です


正しいかどうかはまだ検証していません


では


#include <stdio.h>


int main(void)

{

float a,

a=1.05;


printf("あなたの預金額はただいま%f円です。\n",a*a);

return 0;

}


が実行されると


どのようなことがおこなわれるのでしょうか?


変数aに数値1.05を格納した場合


10進数値1.05は2進数としてコンピュータのメモリに格納されます


そのとき


10進数値1.05は2進数で表そうとしても


2進数の無限小数となるので


コンピュータのメモリに格納しきれません。


そこで


10進数値1.05は 


ある2進数の値Aで1.05に近似された状態でコンピュータのメモリに格納されます



よって


2進数A(1.05に近似された数値)に


2進数A(1.05に近似された数値)をかけあわせたものを


%f出力変換子をつかってprintf出力表示するので


プログラムの実行結果は


あなたの預金額はただいま10500.000000円です。


でなく


あなたの預金額はただいま10499.998745円です。


のように表示されます



もうすこし ここの部分を詳しく説明すると


2進数A×2進数Aの計算結果は


ある2進数値Bとなります

👆

🍓🍓🍓🍓🍓🍓


2進数Aの小数点以下の桁数が多すぎると


2進数A×2進数Aの計算結果は


小数点以下の桁数が長くなりすぎるなるので


2進数A×2進数Aの計算結果は


小さな位の数値は四捨五入のようなものがおこなわれて


短く桁数が調整されて


ある2進数値Bとなっています


2進数A×2進数Aの近似値が


ある2進数値B


となっているわけです


ですので


正確には


2進数A×2進数A=ある2進数値B


ではありません。


2進数A×2進数A

ある2進数値B

には誤差があるというわけです。


🍓🍓🍓🍓🍓🍓


そう


1.05が


ある2進数Aに近似されただけでなく


2進数A×2進数Aが行われると


さらに


2進数A×2進数Aは別の数値である


ある2進数値B


になっているというわけです



その ある2進数値Bを


%f出力変換指定子をつかって


10進数値に変換してprintf出力表示したときの値が


10499.998745となるわけです。


そして この


%f出力変換指定子をつかって


ある2進数値Bを


小数点以下6桁の10進数値に変換してprintf出力表示する段階でも


小数点以下7桁の10進数値に四捨五入がおこなわれてから


      あなたの預金額はただいま10499.998745円です。


が表示されています。


近似が何回かおこなわれているわけです。


手が こんでいますね。


ですので 結局のところ


実際には 


a*a=1.05かける1.05


がおこなわれているのではないのです



  • Xで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

新規登録で充実の読書を

マイページ
読書の状況から作品を自動で分類して簡単に管理できる
小説の未読話数がひと目でわかり前回の続きから読める
フォローしたユーザーの活動を追える
通知
小説の更新や作者の新作の情報を受け取れる
閲覧履歴
以前読んだ小説が一覧で見つけやすい
新規ユーザー登録無料

アカウントをお持ちの方はログイン

カクヨムで可能な読書体験をくわしく知る