天国にいけるC言語入門 シーズン1 パソコン超初心者がゼロから東方風シューティングをつくる編 ver.0.4.15.790 RELIEF
☆☆10進数の実数を2進数で表現する コンピュータで計算をおこなった結果 コマンドプロンプト画面に近似値が表示される訳 をご紹介します。
☆☆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
がおこなわれているのではないのです
新規登録で充実の読書を
- マイページ
- 読書の状況から作品を自動で分類して簡単に管理できる
- 小説の未読話数がひと目でわかり前回の続きから読める
- フォローしたユーザーの活動を追える
- 通知
- 小説の更新や作者の新作の情報を受け取れる
- 閲覧履歴
- 以前読んだ小説が一覧で見つけやすい
アカウントをお持ちの方はログイン
ビューワー設定
文字サイズ
背景色
フォント
組み方向
機能をオンにすると、画面の下部をタップする度に自動的にスクロールして読み進められます。
応援すると応援コメントも書けます