小数点の最下位が正常に表示されません。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ダッシュ

小数点の最下位が正常に表示されません。

#1

投稿記事 by ダッシュ » 17年前

float a=10.2,b=20.2
printf("%f",a+b);

で表示すると、
30.400002
になります。何故でしょうか。

管理人

Re:小数点の最下位が正常に表示されません。

#2

投稿記事 by 管理人 » 17年前

この答えですが、正常です。
ちょっと答えが違ってしまって正解なんです。

ダッシュさんは整数値の比較は==で行ってはいけない事どこかで聞きました?
それは小数というのは完全な数で表す事が出来ないからなんです。
2進数と10進数について高校で習いますよね。

例えば10進数の1は
1(10)
と書き、2進数の1は
1(2)
と書きます。では2についてはそれぞれ
2(10)
10(2)
となります。2進数は0と1しかないので1になると次は次の桁に繰り上がります。
10進数の0~9を2進数とともに見て見ましょう。
0(10)=0(2)
1(10)=1(2)
2(10)=10(2)
3(10)=11(2)
4(10)=100(2)
5(10)=101(2)
6(10)=110(2)
7(10)=111(2)
8(10)=1000(2)
9(10)=1001(2)
ここで、2進数を10進数にするには各桁の2のn乗の和を求めたらいいことになりますね。
例えば7(10)は111(2)です。
111
↑↑↑
↑↑この桁は2の0乗=1
↑この桁は2の1乗=2
この桁は2の2乗=4
1+2+4=7になります。
同様に9(10)=1001(2)はどうでしょうか。
1001
↑↑↑↑
↑↑↑この桁は2の0乗=1
↑↑この桁は2の1乗=2
↑この桁は2の2乗=4
この桁は2の3乗=8
1がたっているのは8の位と1の位。8+1=9となりやはり9に合いますね。

つまり整数はどの値でも(扱える桁数なら)正確に表す事が出来ます。
では小数はどのように表すでしょうか。

0(10)    =0(2)
0.5(10)  =0.1(2)
0.75(10) =0.11(2)
0.875(10)=0.111(2)

このようになります。
先ほどの
0000
↑↑↑↑
↑↑↑この桁は2の0乗=1
↑↑この桁は2の1乗=2
↑この桁は2の2乗=4
この桁は2の3乗=8


0.0000
__↑↑↑↑
__↑↑↑この桁は2の-4乗=1/16
__↑↑この桁は2の-3乗=1/8
__↑この桁は2の-2乗=1/4
__この桁は2の-1乗=1/2

このように計算されます。
つまり小数点1つしか桁を表すビットが無かったら
0.0(2)は0.0(10)
0.1(2)は0.5(10)ですから
10進数で言う0.1~0.5までの値が表せない事になります。
そうなんです。2進数の各桁は
1/2
1/4
1/8
1/16
1/32
1/64
.................となっているため、この和で10進数の小数点を表すと完全な数字が表せません。
ですからfloat型やdouble型の小数点の下位は信用できない値なんです。
まず、
0.5(10)は0.1(2)です。
では
0.1(10)は0.000110011001100110011........(2)
です。無限に続きますが、表せる数には限りがあります。

ところで、とてつもなく大きな数、小さな数のときは0が大量につきますね。

110110100100000000000000000000000 = 1.101101001 * 2^32
0.00000000000000000000000001101101001 = 1.101101001 * 2^-26

^以降は指数を表します。
つまり指数で表せば無駄な0を書かずに済みます。

float型は全24ビットで表しています。
最初の1ビットを符号、次の8ビットを指数、次の23ビットを仮数部として使用しています。
仮数部とは先ほどの例で言えば1.101101001にあたります。
仮数部23ビット、指数部8ビットで表せるはんいの数しか正確に表せないわけですね。

ちなみにdouble型の方が精度もよく、計算速度も速いので、floatは特に使う必要がありません。
doubleの方が桁数が多いのに何故速いかというと、double型のデータを専門に扱う回路が付いているからです。
容量は2倍くいますが、所詮2バイトの違いですからね。double型を使った方がいいと思います。

管理人

Re:小数点の最下位が正常に表示されません。

#3

投稿記事 by 管理人 » 17年前

う、矢印と桁がうまくあってないですね。

01
↑↑
↑②


こういう表記は①は0を、②は1を示していると思ってください。

バグ

Re:小数点の最下位が正常に表示されません。

#4

投稿記事 by バグ » 17年前

どうしても、double型やfloat型の数値同士で比較したい場合は、必要な桁数分だけを文字列に変換して比較するのがポピュラーな方法でしょうか?

管理人

Re:小数点の最下位が正常に表示されません。

#5

投稿記事 by 管理人 » 17年前

いえ、そんなに難しい事をしなくても、比較したい時は2つの数の差を取ればいいんですよ。

ちょっとその前にどれ位値が信用できないものか、こんなプログラムで見てみましょう。

このサンプルは
a=10.1,b=20.2,c=30.3
としてa+b==cの比較を行っています。本来ならa+bはcに等しいはずですから、一致するはずですよね。

#include <stdio.h>
void main(){
  float a=10.1,b=20.2,c=30.3;
  if(a+b==c)
    printf("一致\n");
  else{
    printf("不一致\n");
    printf("a+b=%.13f,c=%.13f\n",a+b,c);
  }
}

実行結果

不一致
a+b=30.3000011444092,c=30.2999992370605


期待した結果は、両者とも30.3になるという事ですが、両者とも完全な30.3という値ではありません。
このように、小さな値は信用できない事がわかりましたね。
小さな値が信用できないのなら、小数点第1位だけ信用しようと思っても、
cの結果のように小さな数が負の方向に間違っているため、小数点第一位ですら違う値になってしまっている場合があります。

そこでこの2つの数を比較するには「2つの数の差」を取ればいいんです。cとa+bを比較したいのなら
c-(a+b)が自分が希望する精度の範囲ならマッチとしてやればいいんです。
上記計算結果から0.001の位は信用できそうですね。
つまり2つの数の差が0.001以内であれば等しいと考えればよさそうです。
こんなサンプルを作ってみました。
absというのは引数の絶対値を計算する関数です。
c-(a+b)の計算結果は負の数の可能性もあるので、絶対値が0.001以内とする必要があります。
このような条件文を書けば一致しますね。


#include <stdio.h>
#include <math.h>
void main(){
  float a=10.1,b=20.2,c=30.3;
  if(abs(c-(a+b))<0.001){
    printf("一致\n");
    printf("誤差%.13f\n",c-(a+b));
  }
}

実行結果

一致
誤差-0.0000019073486


ちなみにfloat型は精度も良くないし、使ってもいいことありませんから精度の高いdouble型を使うようにしましょう。
精度の高い計算を行ったからと言って処理が遅くなる事はありません。

バグ

Re:小数点の最下位が正常に表示されません。

#6

投稿記事 by バグ » 17年前

さっそくの返事、ありがとうございます。
実は私、駆け出しですが、計測関係のプログラマーをやっているもので、かなり精度の高い数値を求められるのですよ…泣けてきます(苦笑)
そういう訳で苦肉の策として編み出したのが、sprintf()で必要な桁数を抜き出して、strcmp()で比較する方法でした…(;^_^A
これだと、誤差が出にくいように思うのですが…

管理人

Re:小数点の最下位が正常に表示されません。

#7

投稿記事 by 管理人 » 17年前

30.0のデータが

30.0000001

となっている場合は文字に変換してもいいかもしれませんが、

29.9999999

になっている時は比較出来ませんよね。

もし差の絶対値比較という方法を用いないのでしたら
0.00001を足して1000倍してint型に変換して比較する方がいいと思います。

プログラムってどう考えるかですからどうやれば最善の処理できるか考えるの難しいですよね。

Justy

Re:小数点の最下位が正常に表示されません。

#8

投稿記事 by Justy » 17年前

 こんばんわ、バグさん。

> どうしても、double型やfloat型の数値同士で比較したい場合
 管理人さんの書かれている通り、差の絶対値が一定値以下を等しいとする判定で行うのが一般的です。
 ただその一定値をどうするかは計算の内容や求められている精度にも影響するので慎重に決定する必要があります。


> 計測関係のプログラマーをやっているもので
 同僚・先輩に相談はされたのでしょうか。
 もしまだされていないのであれば、是非訊いてみた方がいいと思います。
 その手のプログラムであればその会社或いは業界的に標準となっている手法が定められている可能性があります(日本工業規格とか?)。
 ひょっとしたら Double型でも精度が足らず、その会社独自の多倍長小数ライブラリとかがあったりするかもしれません。

管理人

Re:小数点の最下位が正常に表示されません。

#9

投稿記事 by 管理人 » 17年前

NASAとかでの計算っていったい何ビットで計算しているんでしょうw
すごく細かな数まで計算してそうですね~。

Justy様がいらっしゃると(全くそんな事するつもりはありませんが)いい加減な事が言えないのでカキコ見てるとヒヤっとしますw
回答に書いた方法が本当に一番いい方法だったのかな?もっといい方法があったんじゃないのかとか。
指摘してくださる事はいい刺激にもなり、また万が一私の回答が間違っていたら正解を書いてくださるのでとても助かってます。
所詮青二才大学生がボランティアでやってるサイトですからね!
しかし責任持って、正しい事を書いていく所存ですのでこれからもサポートよろしくお願いします♪

Justy

Re:小数点の最下位が正常に表示されません。

#10

投稿記事 by Justy » 17年前

 こんばんわ。

>回答に書いた方法が本当に一番いい方法だったのかな?もっといい方法があったんじゃないのかとか。
 いやいや、いつもベストな回答をしてらっしゃいますよ。
 質問者さんに丁寧に回答していますし、わかりやすいです。

 私が回答するときは、出来るだけ別の方面からのアプローチをするようにしています。
 でも難しすぎてもダメなんでそのさじ加減が難しいです。


>しかし責任持って、正しい事を書いていく所存ですのでこれからもサポートよろしくお願いします♪。
 こちらこそ、よろしくお願いします。

管理人

Re:小数点の最下位が正常に表示されません。

#11

投稿記事 by 管理人 » 17年前

> いやいや、いつもベストな回答をしてらっしゃいますよ。

Justyさんにそういってもらえると他の方に言ってもらえるのとは違った安心感があります^^

> でも難しすぎてもダメなんでそのさじ加減が難しいです。

そうなんですよね。プログラムに慣れてくると、初心者の頃の事を忘れてしまうんですよね。
自分は1から説明しているつもりでも、聞いてるほうからしたら5位から説明されてる感じがして。
例えば
「それはライブラリの問題だよ」
「専用のライブラリをインクルードしないとだめだよ」
「APIの関数の問題だね」
そんな回答をずらずらいわれても初心者としてはわからないんですよね。
え?ライブラリ?API?とかって。
ウェブの回答ってわざとアプリケーションプログラムインターフェイスだね。
とかわざとらしく難しく言ったり。
とにかく上級者の説明はわかりにくかったです。
そんな事がないように、さらっと使った専門用語が実は難しい言葉だったりしないよう気をつけてます。
なるべく自分が昔苦労していた頃を思い浮かべながら回答するようにしています。
インクルードとか、ライブラリって言葉、慣れてしまうと専門用語だと思わなくなってきますモンね。

閉鎖

“C言語何でも質問掲示板” へ戻る