0.1 + 0.2 + 0.3・・・・・・
このように行うプログラムを2通りに分けて処理をして見ました。
以下にソースを載せます。
#include <stdio.h>
int main()
{
float sum, i;
float sum2;
int f;
for(i=0.1, sum=0.0; i<=100.0; sum+=(float)i, i+=0.1);
printf("%f\n", sum);
for(f=1, sum2=0.0; f<=1000; sum2+=(float)f/10.0, f++);
printf("%f\n", sum2);
return 0;
}
初めのfor文と2番目のfor文では同じ処理を行っているのですが、計算結果が微妙に異なって出力されてしまいました。
理由が分かる方は教えてもらえないでしょうか?
No.5ベストアンサー
- 回答日時:
No.3 です。
投稿に少々間違いがあって、本当は、6 の時ではなくて、3 で既に誤差が明らかになりますね。正しくは、1.00 と 0.999 です。
さて、
> しかし、分からない部分が一箇所。2列目は0.333自体をどん
> どん足していく処理で、3列目は1/3の結果を毎回足しています。
これが実は誤解です。
3列目は、1/3 を足しているのではなく、「1ずつ足したものを毎回3で割っている」のです。
ここにはいろいろな形の誤差が現れています。
まず、1/3 が正確に表現できないので、0.333 で近似しています。この時点で、0.0003333... という大きさの誤差が出ます。これがいわゆる打ち切り誤差(丸めの誤差)です。
3列目の各項は 1, 2, 3, 4... という「誤差のない」数字を3で割っていますから、この打ち切り誤差だけが表面に出てきています。
これに対して、2列目は、0.333 という誤差のある数字を順次足していますから、最初の打ち切り誤差が累積します。
打ち切り誤差の大きさは、「最後の桁に対して、±1以下」です。
つまり、1/3 を 0.333 と表現した場合、この誤差は、0.001 を超えることはありません。3列目に現れている誤差はこの部分だけなので、各項は、最初の整数(1, 2, 3 ... )を3で割った値の「最後の桁に対して±1以下」の誤差です。
ですから、小数点が3桁確保できている範囲では、誤差は 0.001以下になっています。これに対して2列目は、0.00033333 という大きさの誤差が各項ごとに累積されます。
こういうことです。
以下おまけ。
さらにもうひとつ「情報落ち」という誤差が出てきます。
これは、ここに示した範囲では明らかにはなっていませんが、もしも、このまま計算を進めて 300行を超えると、明らかになります。
つまり、有効数字3桁の世界では3列目の方法では
300/3 = 100, 301/3 = 100, 302/3 = 100, 303/3 = 101
となって、誤差を伴いながらも、数字は変化します。しかし2列目の方法では、
100 + 0.333 = 100 のままですから、ここを超えると永久に数字が変化しなくなります。
このように、足す数と足される数の違いが大きいとさらに誤差が出てくることになります。
返答ありがとうございました。ためしに以下のプログラムを作成しました。
for(i=0.333, sum=0.0; i<=3; sum+=(float)i, i+=0.333){
printf("%.3f\n", sum);
}
for(f=1, sum2=0.0; f<=10; sum2+=(float)f/3.0, f++){
printf("%.3f\n", sum2);
}
AsanoNagiさんがおっしゃられたとおりになりました。
少しまとめていってしまえば上のfor文では毎回0.0003333の誤差が累積していくのに対し、下のfor文では0.3333333333333333・・・と表現できるところまでの少数桁を毎回加算して、表示のときに四捨五入をして
切り詰めて表示をしているため、まず3回目のところで、1.000と0.999の値が出てしまうんですね。
No.4
- 回答日時:
IEEE754に限定すると、0.1は表現不可能な値です。
2進数で表現すると
0.000110010101010101010101010
0.000110010101010101010101011
この2つの値の間に0.1があり、0.1は2進数では無限小数です。
我々にとってのキリのいい少数は大抵表現できないので、誤差は出るものと思うしかありません。
返答ありがとうございました。
固定小数点の場合は確かにおっしゃられる通りになりますね。
0.1を固定小数点の2進数表現で表す場合、2をかけていけば4回目でやっと1が小数点でなくなるので、記述されたとおりになりますね。
ぎゃくにコンピュータで正確に表現できる数は、0.5, 0.25, 0.75・・・などですね。
アドバイスを頂きありがとうございました。
No.3
- 回答日時:
これは、2進数表現の浮動小数点の世界では、
1)÷ 10 というのは、たいていの場合割り切れない
2)2進数表示で「有効桁数」が決まっている
ことによります。
慣れ親しんだ 10進数で似たようなことをやってみると
こうなります。
ここでは、10進数の世界では大抵割り切れない÷3を
10進数で有効桁数3で計算をしてみます。
※計算している数値は違いますが、処理の内容は似た
ようなことになります。
1 0.333 0.333
2 0.666 0.666
3 0.999 0.999
4 1.33 1.33
5 1.66 1.66
6 1.99 2.00
7 2.32 2.33
8 2.65 2.66
9 2.98 3.00
10 3.31 3.33
2番目の列が、1/3を 10進3桁で丸めた値 0.333 を
順次足したものです。
こちらが、ご質問の、最初の例に相当します。
あくまでも、「有効桁数」が固定なので、途中から、
小数点以下が2桁になってしまっています。
しかも、ここまで来ると、実質(0.333 ではなく)0.33
しか足すことができません。
3番目の列が、最初の数字を3で割って、3桁で丸めた
ものです。
こちらがご質問の、2番目の例です。
こちらは、誤差の蓄積がないので、それぞれの数字は、
最初の数字を3で割ったもの(を丸めたもの)になって
いると思います。
(これでも、丸めの誤差があるので、足し算をした結果
は誤差を含みます)
それぞれの数字を足してゆくと、結果が異なってくるの
がわかると思います。
誤差を少なくするためには、最初の列の数字をすべて足
したあとで、3で割るのがベストです。
これをご質問の例に適用すると、
1 + 2 + ... を計算した後で、計算結果を 10で割るのが
最も正確と言うことになります。
質問の例では、後者のほうが精度は良くなります。
返答が遅れてしまい、申し訳ありませんでした。
そして理解しやすい図まで提示してもらい感謝します。
しかし、分からない部分が一箇所。2列目は0.333自体をどんどん足していく処理で、3列目は1/3の結果を毎回足しています。
6行目で初めて差が出てくるのですが、どうしてこの6行目では0.001の差が出てくるのでしょう?
一応二つ共に0.333を途中まで足し、その後有効桁数の壁にかこまれ0.33の加算となっているので、差は出てこないように思われるのですが・・・・
No.2
- 回答日時:
実数をどうやって表現しているのか?に起因する根源的な問題です。
通常は浮動小数点方式と言う指数部と実数部に分けて表現する方法をとりますが、この実数部の基底値が2であるため1/3や1/5,1/10等の2で重み付けできない値を扱うと丸め誤差が発生します。
丸め誤差の蓄積の差が計算結果の差になって現れます。
誤差を出したくないのであれば桁上げして(全体を10倍して)整数演算を行うのが簡単かと思います。(桁あふれに注意する必要がありますが)
返答遅れて申し訳ありませんでした。
浮動小数点演算や、固定小数点演算のことをすっかり忘れていました。
アホですね自分はw
解決策までアドバイスを頂ありがとうございました。
10倍してから計算をし、その後10で割れば一応誤差のない計算をすることができますね。
No.1
- 回答日時:
> for(i=0.1, sum=0.0; i<=100.0; sum+=(float)i, i+=0.1);
i+=0.1 で正確に0.1ずつ足すことはできないので誤差が出ます。
(なお、2つめの方法でもより小さいですが誤差は出ます。)
for(i=0.1, sum=0.0; i<=100.0; sum+=(float)i, i+=0.1)printf("%12.8f\n",i);
のようにして確認してみては。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/12】 急に朝起こしてきた母親に言われた一言とは?
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・好きな「お肉」は?
- ・あなたは何にトキメキますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
100桁の計算ができなくて困って...
-
有効数字について 以前質問をし...
-
VBAでミリ秒まで出力する方法
-
VB.net Double と...
-
Excel 計算結果誤差
-
O(n log n)について2
-
三菱シーケンサ(Aシリーズ)で...
-
16進数 加算 減算 C言語
-
エクセル計算 答えは同じなの...
-
加算と減算で乗算と除算を表現...
-
2進数の足し算(C言語)
-
floatの有効桁数がわからない
-
距離から緯度経度を求める方法
-
マイナスからプラスへ転じた時...
-
プログラミング初心者です。 Py...
-
C言語での引数の省略方法
-
Aの値からBの値を除するとは??
-
Enterキーを押されたら次の処理...
-
C言語 エラーの原因がわからな...
-
構文エラー";"が型の前にあり...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
O(n log n)について2
-
c languageで 簡単な質問があ...
-
16進数 加算 減算 C言語
-
有効数字について 以前質問をし...
-
ExcelのINT関数の計算結果がお...
-
除算を使わずに10で割りたい。
-
VB.net Double と...
-
2進数の足し算(C言語)
-
ExcelでPC(パソコン)によって...
-
三菱シーケンサ(Aシリーズ)で...
-
EXCELの関数"STDEV(標準偏差)"...
-
”/”を使わずに割り算したいんで...
-
floatの有効桁数
-
VBAでミリ秒まで出力する方法
-
コンピューターは指数関数をど...
-
距離から緯度経度を求める方法
-
VB6.0での小数点の扱いについて
-
時刻の比較
-
どんな数字を入力してもaverage...
-
powf を使わずにべき乗を計算
おすすめ情報