この人頭いいなと思ったエピソード

#include <stdio.h>

int main()
{
float height,weight;

printf("身長と体重を入力してください。\n");
scanf("%f",&height);
scanf("%f",&weight);

printf("身長は%fセンチ:体重は%fキロです。\n",height,weight);

return 0;
}

上記のようなプログラムを作って、身長には175.1体重には56.1という入力を行なって実行したところ、
身長は175.100006センチ:体重は56.099998キロです。
という結果が返ってきました。
heightとweightをdouble型で宣言したところ(もちろんscanf文の変換仕様は%lfにしています。)、結果は
身長は175.100000センチ:体重は56.100000キロです。
と私が、期待していたものが返ってきました。なぜfloat型だと期待通りの結果が返ってこないのでしょうか?ご教授お願い致します。

A 回答 (3件)

ご質問は,いわゆる2進数計算のまるめ誤差のことでしょうか.



問題は,2つあると思います.
 ・循環小数を有限ケタで切ること

 ・何桁まで表示するか?

という問題です.


・循環小数を有限ケタで切ること
コンピュータの内部は,一部を除き,
「2進数」で処理されています.

少数点以下の表し方の中には,10進数だときっちり表せるものが,2進数では,循環小数のようになって有限のケタでは正確に表せない場合が出てきます.

つまり,10進数だと
 0.1 (10^-1)
0.01 (10^-2)
0.001 (10^-3)
として,区切りですが,
2進数だと,
 0.5 (2^-1)
0.25 (2^-2)
0.125 (2^-3)
0.0625 (2^-4)
0.03125 (2^-5)
0.015625 (2^-6)
0.0078125 (2^-7)
0.00390625 (2^-8)
となり,たとえば,0.1は
 0.1(10進数) = 0*0.5 + 0*0.25 + 0*0.125 + 1*0.0625 + 1*0.03125 + 0*0.015625 + 0*0.0078125 + 1*.00390625 + ...

= 0.00011001... (2進数)
となり,2進数の世界では循環小数となります.
これを有限のケタで切ると誤差ができます.

つまり,2進数の有限のケタであらわした
0.1(10進数)を10こ足しても,
 1になりません.

・何桁まで表示するか?
そうはいっても,単精度でも,通常の使用には十分
耐える精度は持っています.
 表示桁数を少なくして,四捨五入してしまえば
みためには正解にみえるものも表示できます.
 多くの計算結果は,そのようにして表示されています.

 たとえば,さきほどの
0.00011001... (2進数)
    = 0*0.5 + 0*0.25 + 0*0.125 + 1*0.0625 + 1*0.03125 + 0*0.015625 + 0*0.0078125 + 1*.00390625
= 0.09765625 (10進数)
です.少数第3位で四捨五入すれば,
   0.1 (10進数)
です.

ご質問では,倍精度ではただしいという結果が出ていますが,0.1は2進数では循環小数ですから,倍精度といえども内部表現は不正確です.
ここでの差は単なる表示桁数の差でしょう.
単精度は,10進数で7ケタ
倍精度は,10進数で14ケタ
の精度を持っています.ご質問では,9ケタ程度表示していますので,その差がでていると思います.

ご質問者は,「電卓だとちゃんと表示するじゃないか」と思われると思います.
実は,電卓などの会計にしようされるものには,上記のような不正確さは許されません.
よって,「10進補正」といいまして,
10進数で正確に表せるように内部計算を10進数で行っているのです.
このような補正をすれば,計算速度はかなり落ちますが,10進数であらわしたものは正確に計算できます.
事務用のマシンでこの機能をもったものが多いようです.
おおくのコンピュータは,処理速度が命ですから,
2進数で計算していますだから,このような誤差は認識した上で使用すべきでしょう.



この誤差を避ける方法として,「固定小数点方式」
といいまして,
 0.1を1000000 * 0.0000001
としてあらわし,計算はすべて,整数でおこない表示だけを小数点をつけてあらわす方法です.この方法だと,整数で計算するので,上記の誤差はでません.
ただし,この方法では,極端に大きな数字
たとえば,6.02*10^24 を表すことが出来ません.
極端に小さな数字 8.854*10^-24などもあらわせません.

また,ちなみに,最近のCPUは,浮動小数計算専用のハードを実装していて,単精度も倍精度も計算時間は変わりません.ただし,メモリは倍必要になります.メモリさえゆるせば,私はすべて倍精度としています.
また,C言語では,言語の簡単化のためか,変数にfloatは存在していますが,計算時の内部処理はすべていったん倍精度に変換されてから行われます.
(単精度を倍精度に変換しても,精度はあがりませんが.)
    • good
    • 0
この回答へのお礼

ものすごく親切な回答をしていただきありがとうございます。電卓では補正をおこなっているのは知りませんでした。最近のパソコンではメモリーも大容量積まれているので、doubleでも問題ないようですね。勉強になりました。

お礼日時:2004/05/20 18:25

doubleも


厳密に175.100000ジャストではありません。
そのことについては浮動小数点を理解してください。
http://www.jtw.zaq.ne.jp/kayakaya/new/kihon/text …
IEEE形式浮動小数点について
http://www.cc.nao.ac.jp/vppman/HTML/japan/langFo …

doubleで175.100000と表示されるのは
第7位が四捨五入されて175.100000になっているだけです。
floatの有効数字は7桁
10.000000 この場合9桁の有効数字がなければ
表示上誤差がでる可能性があります。

doubleも17桁以上表示させれば誤差を確認できます。
double h = 175.100000;
printf("h=%.17lf\n",h);

結果
h=175.09999999999999430
    • good
    • 0
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!