C言語関係掲示板

過去ログ

No.1114 視力の分布を求めるプログラム(実数の誤差)

[戻る] [ホームページ]
No.1871

プログラムがうまく動きません
投稿者---ひよこ(2004/06/01 20:08:29)


ひよこです。
スーパー初心者になります。(コンパイラBCC5.5)

下のプログラムのどこが悪いのか教えていただきたいです。

#include <stdio.h>

#define VMAX 21

typedef struct{
    char name[40];     //名前
    int height;        //身長
    double vision;     //視力
}PhysCheck;            //肉体チェック

//視力の分布を求める
void dist_vision(const PhysCheck dat[],int n,int dist[])
{
    int i;

    for(i=0;i<VMAX;i++)
        dist[i]=0;

    for(i=0;i<n;i++)
        if(dat[i].vision>=0.0 && dat[i].vision<=VMAX/10.0)
            dist[(int)(dat[i].vision*10)]++;
}

int main(void)
{
    int i;
    PhysCheck x[]={
        {"松嶋奈々子",168,0.1},
        {"竹内結子",163,0.2},
        {"小西真奈美",167,0.3},
        {"広末涼子",158,0.4},
        {"鈴木亜美",155,0.5},
        {"吉岡美穂",161,0.6},
        {"白石ひより",153,0.7},
        {"×ビクトリア夫人",166,0.8},
};

    int nx=sizeof(x)/sizeof(x[0]);
    int vdist[VMAX];

    dist_vision(x,nx,vdist);

    printf("視力の分布\n");
    for(i=0;i<VMAX;i++)
        printf("%3.1f〜:%2d人\n",i/10.0,vdist[i]);

    return 0;
}


余分な所は省略しました。
結果がうまく出力されません。

どなたかご教授お願いいたします。m(_ _)m


No.1872

Re:プログラムがうまく動きません
投稿者---かずま(2004/06/01 20:32:50)


>   dist[(int)(dat[i].vision*10)]++;

    dist[(int)(dat[i].vision * (1 + 1e-15) * 10)]++;

--------------------------------------
#include <stdio.h>

void p(double x) { printf("%.20f\n", x); }
void q(double x) { printf("%.20f\n", x * (1 + 1e-15)); }

int main(void)
{
    p(0.1); p(0.2); p(0.3); p(0.4); p(0.5); p(0.6); p(0.7); p(0.8);
    puts("---");
    q(0.1); q(0.2); q(0.3); q(0.4); q(0.5); q(0.6); q(0.7); q(0.8);
    return 0;
}



No.1874

Re:プログラムがうまく動きません
投稿者---ひよこ(2004/06/01 22:23:14)


とてもわかりやすい回答ありがとうございます。

VC++5.0でも試してみたところ、かずまさんに書いていただいたプログラムの
結果は同じような感じなのに、はじめに自分が書いたプログラムの結果は
ちゃんと出るんですがこれはどういうことなんでしょうか。
再度の質問すみません。



No.1879

Re:プログラムがうまく動きません
投稿者---ひよこ(2004/06/02 02:45:13)


わかったと思ったのですが全然わかっていませんでした。
だって
#include <stdio.h>

int main(void)
{
    printf("%.17f\n",0.3);

    return 0;
}


これが0.29999999999999999で
#include <stdio.h>

int main(void)
{
    printf("%.16f\n",0.3);

    return 0;
}


これが0.3000000000000000
だということすらわからないんですもの。

実数は今まで扱ってきませんでした。
やり直します。(しょぼーん)




No.1880

Re:プログラムがうまく動きません
投稿者---ひよこ(2004/06/02 06:33:42)


#include <stdio.h>

int main(void)
{
    printf("%f\n",0.3);
    printf("%f\n",0.6);

    return 0;
}

が、0.300000と0.600000というのは
表面上はこのように表示されているけどコンパイラ(BCC5.5)は
0.29999台や0.59999台に本当は解釈していてそれで当初のプログラムは
うまく動かなかった。

だけどVC++5.0の方は当初のプログラムでも動いたということは
小数部の評価がBCC5.5よりも精度なしならば浅い所で解釈されている
からうまく動いた。

ということは、BCC5.5の方がVC++5.0より深い所(より厳密に)で
実数の計算をしているということ。

間違っているかもしれないけど、そう思った。




No.1881

Re:プログラムがうまく動きません
投稿者---たか(2004/06/02 12:25:52)


>が、0.300000と0.600000というのは
>表面上はこのように表示されているけどコンパイラ(BCC5.5)は
>0.29999台や0.59999台に本当は解釈していてそれで当初のプログラムは
>うまく動かなかった。
>
>だけどVC++5.0の方は当初のプログラムでも動いたということは
>小数部の評価がBCC5.5よりも精度なしならば浅い所で解釈されている
>からうまく動いた。
>
>ということは、BCC5.5の方がVC++5.0より深い所(より厳密に)で
>実数の計算をしているということ。
>
>間違っているかもしれないけど、そう思った。

IEEE754形式の浮動小数点数は一般的に0.5のn乗若しくはその和は正しく
表せますが、それ以外の数は循環小数による誤差が生じます。従って浮動
小数点数の比較を比較演算子を使って行うのは一般的には良くないアイディアです。

例えばforループに0.1刻みの浮動小数点数を使用すると一回多く回る、な
どの不都合が生じます。その場合はint型で回し、文内ではintから浮動小数
点数を生成するようにします。(もっともこれは丸め誤差の累積による)

例外はニュートン法などの数値演算で収束を判定する場合で、これは比較
するしか手がありません。

今回の場合は多少プログラムが見にくくなりますが、視力を10倍したint型
で下駄を履かせる事によりうまく動きます。これがどうしてもお嫌でした
ら、標準のC言語の範囲内では困難なのでBCD演算をするライブラリを利用
する事になります。金融計算など端数に誤差が出てもらっては困る場合に
実際に使われています。

#include <stdio.h>

#define VMAX 21

typedef struct {
  char name[40];  //名前
  int height;     //身長
  int vision;     //視力 * 10
} PhysCheck;      //肉体チェック

//視力の分布を求める
void dist_vision(const PhysCheck dat[], int n, int dist[])
{
  int i;

  for (i = 0; i < VMAX; i++)
    dist[i] = 0;

  for (i = 0; i < n; i++)
    if (dat[i].vision >= 0 && dat[i].vision < VMAX)
      dist[dat[i].vision]++;
}

int main(void)
{
  int i;
  PhysCheck x[] = {
    {"松嶋奈々子", 168, 1},
    {"竹内結子", 163, 2},
    {"小西真奈美", 167, 3},
    {"広末涼子", 158, 4},
    {"鈴木亜美", 155, 5},
    {"吉岡美穂", 161, 6},
    {"白石ひより", 153, 7},
    {"×ビクトリア夫人", 166, 8},
  };

  int nx = sizeof(x) / sizeof(x[0]);
  int vdist[VMAX];

  dist_vision(x, nx, vdist);

  printf("視力の分布\n");
  for(i = 0; i < VMAX; i++)
    printf("%3.1f〜:%2d人\n",i / 10.0, vdist[i]);

  return 0;
}



No.1884

ありがとうございます。
投稿者---ひよこ(2004/06/02 13:46:18)


自分は初心者だけに、たかさんのお話は初耳でした。
ですが、説明の内容は本当に良くわかりました。

丁寧に解説していただいて恐縮です。
ありがとうございました。