C言語関係掲示板

過去ログ

No.381.変数の位置及び浮動小数点数の扱い

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

変数の位置について。
投稿者---kikuchi(2002/08/30 10:08:57)



はじめまして。大学で地球科学を学んでいます。
卒論で、熱力学の計算から、地球内部について
研究しようと、C言語をはじめました。
学部3年のうちに、ある程度のレベルまでもっていきたいと
思っております。まだまだ初心者ですが、
よろしくお願いします。

さて、さっそくで申し訳ないのですが質問させてください。
以下、2次方程式の解を求めるプログラムを作成しました。
ax^2+bx+c=0をとくものです。a=0、a≠0で場合わけし、
さらに、こまかい場合分けをしました。
a≠0のとき、つまり完全に2次方程式となるときには、
判別式Dをつかって、場合分けをしました。
ここで、Dをまず最初に、double型で宣言しました。

その後で、D=b*b-4*a*c; としたいのです。
これの位置に問題がありました。
Dを宣言した直後に、これを記載したところ、
まったく変な結果となってしまいました。
そこで、現在の位置(Dを使う直前)に直したところ
うまくいきました。

なぜ、これでうまくいったのか、疑問なのです。
変数を定義するとき、どこがベストである、
ということはあるのでしょうか。

よろしくお願いいたします。
以下、実際のソースです。

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

int main(void)
{

        double a,b,c,D,x1,x2;
        double i,z1,z2;


        printf("ax^2+bx+c=0を求める。");
        printf("aを入力>>");
        scanf("%lf",&a);
        printf("bを入力>>");
        scanf("%lf",&b);
        printf("cを入力>>");
        scanf("%lf",&c);

        if(a==0.0)
        {
                if(b==0.0)
                {
                        if(c==0.0)
                        {
                                printf("0=0となり、意味がない。");
                        }
                        else
                        {
                        printf("%f=0となり、矛盾。",c);
                        }
                }

                else
                {
                        x1=(-c)/b;
                        printf("%fx+%f=0の解は\n",b,c);                      
                        printf("x=%f",x1);
                }
        }

        else 
        {
                D=b*b-4*a*c;

                if(D>0)
                {
                        x1=(-b+sqrt(D))/(2*a);
                        x2=(-b-sqrt(D))/(2*a);

                        printf("%fx^2+%fx+%f=0の解は\n",a,b,c); 
                        printf("解は、%f,%f",x1,x2);
                }
                else if(D==0.0)
                {
                        x1=(-b)/(2*a);
                        printf("%fx^2+%fx+%f=0の解は\n",a,b,c);                     
                        printf("重解となり%f",x1);
                }
                else 
                {
                        i=sqrt(-D);
                        z1=(-b)/(2*a);
                        z2=i/(2*a);

                        printf("%fx^2+%fx+%f=0の解は\n");                     
                        printf("x=%f+%f i ,%f-%f i ",z1,z2,z1,z2);
                }
        }

  return 0;

}

/*  
 D=b*b-4*a*c; の位置に対する疑問
 これを、doubleで定義した直後にもってくると、うまくいかなかった。
 それを、現在の状態にすると、うまく作動している。
 これは、なぜか。
 具体的には、a!=0の場合において、すべてD=0として計算されてしまい、
 正しい値が求められない。
*/



                        


No.2544

Re:変数の位置について。
投稿者---PSB(2002/08/30 12:08:11)


もしかして

double a,b,c,D,x1,x2;
double i,z1,z2;

D=b*b-4*a*c;

こんな感じですか?
ならDが変な値になるのも当然なんですけど・・・。

No.2545

Re:変数の位置について。
投稿者---kikuchi(2002/08/30 17:02:23)


>もしかして
>
>double a,b,c,D,x1,x2;
>double i,z1,z2;
>
>D=b*b-4*a*c;
>
>こんな感じですか?
>ならDが変な値になるのも当然なんですけど・・・。

そのとおりです。・・・。

あぁ、そうか!
これでは、まだa,b,cともに入力されていない状態だから
変数a,b,cには、別のデータが入り込んだ、というわけですね!
なるほど、納得しました。

どうもありがとうございました。
今後ともよろしくおねがいします。

それでは。

No.2546

Re:変数の位置について。
投稿者---TDa(2002/08/30 18:22:43)


こんにちは。

このコードでは問題はないかもしれないですが潜在的に危うい点がありますので
触れさせてください。それは
if(a==0.0)
この部分です。一般的には浮動小数型を==で比較するのはやばいと思っていた方がいいです。

たとえば下記のコードをコンパイルして実行してみてください。

#include <stdio.h>

int main(void)
{
double x = 1.00000000000000001;
double y = 1.0;

if (x == y) {
printf("xとyは等しい\n");
} else {
printf("xとyは等しくない\n");
}

return 0;
}

処理系によって結果は異なりますがbcc32、vc++6, LSIC86ではどれも
"xとyは等しい"
と表示されます。ここではdoubleを使用しましたがfloatだとさらに有効桁が小さくなります。
自然科学では浮動小数型を多用することになろうかと思いますので常に有効桁や誤差を意識して
コーディングする必要があります。

ちなみに一般的には浮動小数型の同一性をどう判断するかといいますと
if (x - y < DBL_EPSILON)
ここでDBL_EPSILONというのはfloat.hで定義されていて 1.0 + x != 1.0が保証されている
最小のxです。
ちなみにbcc32の場合
#define DBL_EPSILON 2.2204460492503131E-16
となっているので x - y < DBL_EPSILONを使用しても正しい結果になりません。


No.2547

Re:変数の位置について。
投稿者---TDa(2002/08/30 19:59:59)


>if (x - y < DBL_EPSILON)
絶対値をつけるのを忘れていました。
fabs(x - y) < epsilon
ですね。上ではDBL_EPSILONを使いましたが必要な有効桁に応じてepsilonの
値を変えるのがよいでしょう。

No.2549

Re:変数の位置について。
投稿者---kikuchi(2002/08/30 21:41:54)


TDaさん、こんにちは。
丁寧に教えてくださり、ありがとうございます。

確かに、x=Yと表示されました。
こうした例を教えていただいたおかげで、
今後、意識しながら、勉強を続けることができます。
プログラミングの際も、心がけます。

また、よろしくお願いします。
ありがとうございました。


No.2548

Re:変数の位置について。
投稿者---かずま(2002/08/30 21:11:46)


printf の書式で、double の出力については、この問題の場合、
"%f" よりも "%.16g" をお薦めします。

また、

    x1=(-b+sqrt(D))/(2*a);
    x2=(-b-sqrt(D))/(2*a);

のところは、

    if (b < 0)
        x1 = (-b + sqrt(D)) / (2 * a);
    else
        x1 = (-b - sqrt(D)) / (2 * a);
    x2 = c / (a * x1);

と書いたほうがよいでしょう。
なぜなら、前者の場合、桁落ちによる計算誤差のため不正確な値が出るからです。
例えば、a = 1, b = -123456789012345, c = 0 の場合を試してみてください。


No.2550

Re:変数の位置について。
投稿者---kikuchi(2002/08/30 21:52:41)


かずまさん、こんにちは。
アドバイス、ありがとうございます。

>"%f" よりも "%.16g" をお薦めします。
%e、%f、%gの使い分けがうまくできません。
よろしければ、どのような場合、これらを使い分ければ
いいか教えてください。
ちなみに、私は「C言語によるプログラミング基礎編第2版」
(オーム社)を使っています。
ここの部分が少々わかりづらかったのです。

>前者の場合、桁落ちによる計算誤差のため不正確な値が出るからです。
桁落ちの心配をしなければいけませんね!
ありがとうございます。
今後、しっかりと意識していきます。