C言語関係掲示板

過去ログ

No.514.電卓

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

電卓
投稿者---由兎(2002/12/22 20:12:53)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

main()
{
int i;
float suuji;
char wk[32];
char c;
char kigou;

i=0;
kigou=0;

while( 1 ){
c=getch();

if( ('0'<=c && c<='9') || c=='.' ||
c=='+' || c=='-' || c=='*' || c=='/' || c=='=' )
putchar(c);
else
continue;

if( ('0'<=c && c<='9') || c=='.' ){
wk[i]=c;
i++;
continue;
}

if( (c=='+' || c=='-' || c=='*' || c=='/' || c=='=') &&
kigou==0 ){
wk[i]='\0';
i=0;
suuji=atof(&wk[0]);
}

if( (c=='+' || c=='-' || c=='*' || c=='/' || c=='=') &&
kigou!=0 ){
wk[i]='\0';
i=0;
}

if( kigou=='+' )
suuji+=atof(&wk[0]);

if( kigou=='-' )
suuji-=atof(&wk[0]);

if( kigou=='*' )
suuji*=atof(&wk[0]);

if( kigou=='/' )
suuji/=atof(&wk[0]);

if( c=='=' )
break;

kigou=c;
}

printf("%f\n",suuji);

return(1);
}
というものを作ってみたのですが
例えば5+4*2=と入力すると本当なら13なのですが
18になってしまいます。
どのようにしたら良いでしょうか?

No.4010

Re:電卓
投稿者---silver fox(2002/12/22 21:16:45)


> if( kigou=='+' )
> suuji+=atof(&wk[0]);
>
> if( kigou=='-' )
> suuji-=atof(&wk[0]);
>
> if( kigou=='*' )
> suuji*=atof(&wk[0]);
>
> if( kigou=='/' )
> suuji/=atof(&wk[0]);
>
> if( c=='=' )
> break;
>
> kigou=c;
> }
・・・
>というものを作ってみたのですが
>例えば5+4*2=と入力すると本当なら13なのですが

13となる為の計算順序はご存じですか?
上記のif文による演算工程がそれとあっていますか?
問題はそこですね。

 仕様上どこまでを要求されているのかが分かりませんから深くは突っ込めませ
んが軽く言ってしまうと

1.小数点が複数個入力出来てしまう。
2.バッファは32バイトと限られてはいるがiの値が32異常になる可能性がチェ
  ックされていない。

などありますよね。

 だとすると演算子の優先順位の解析をするステップを組み込まないのであれば
複数の演算はしないとかにするしかないですよね。

 それをあえて組み込もうとすると結構大変だと思いますよ。数式の構文解析を
どこまでするかをまずお決めになってからの方がよろしいかと思います。

 やり始めると"("や")"のネストの解析等も必要になってくると思います。やっ
てみれば楽しいと思いますけど。

No.4011

Re:電卓
投稿者---silver fox(2002/12/22 21:35:31)


解析ついでにリスト整理してみました。
 できれば人にソースリストを見せるのであれば分かりやすいようにある程度コ
メントを付けておいて下さい。それにコメントを入れていると自分でもやってい
る処理の間違いに気がつくことは多々あると思います。バグを減らすことにもな
りますよ。

while(1){
    c=getch();
    // 数字、小数点、演算記号だったら画面に表示
    if( isdigit(c) || c=='.' || c=='+' || c=='-' || c=='*' || c=='/' || c=='=' )
        putchar(c);
    else
        continue;            // 次の文字を入力
    // 数字、小数点だったらwk[i]にセット
    if( isdigit(c) || c=='.' ){
        wk[i] = c;
        i++;
        continue;            // 次の文字を入力
    }
    // 演算記号でkigouが0だったらwk[i]に0をセット
    if( c=='+' || c=='-' || c=='*' || c=='/' || c=='=' ){
        wk[i] = '\0';
        i = 0;
    }

    if( kigou == 0 )    suuji =  atof(wk);    // 最初の数字を変換
    if( kigou == '+' )  suuji += atof(wk);    // 次が'+'だったら加算
    if( kigou == '-' )  suuji -= atof(wk);    // 次が'-'だったら減算
    if( kigou == '*' )  suuji *= atof(wk);    // 次が'*'だったら乗算
    if( kigou == '/' )  suuji /= atof(wk);    // 次が'/'だったら除算
    if( c == '=' )      break;                // 次が'='だったら終了
    kigou = c;
}


No.4023

Re:電卓
投稿者---由兎(2002/12/23 01:17:11)


コメント入れたほうがわかりやすいですね。
考えてみたのですが・・・
suuji[i]というものを作って入力した数字を
順番にsuuji[0],suuji[1]・・・という風にして
5+4*3を4*3+5のようにソーティングはできますか?
できなさそうですね・・・−0−;

No.4025

Re:電卓
投稿者---silver fox(2002/12/23 01:52:29)


>できなさそうですね・・・−0−;

 出来ないとあきらめるのは早いのですよ。なんかみなさん一気に目的に向かっ
て突き進むから苦難に感じるのではないかと思います。
取り敢えず一つずつ作ってみたらいかがですか。

まずは数字入力の関数なんかを。
規則としては
1.1文字めは数字あるいは小数点。(0.1は当然として.1もOKとする)
2.2文字以降は前に小数点があったら数字のみ。
3.終端はどちらがいいでしょう?小数点があったらもう一文字入力させる。あ
  るいは1.でもOK?でも1番の条件に.1もOKなんて決めたから'.'一文字で通
  っちゃう可能性があるからやはり付けさせた方がよさそうね。
4.エディタを作る訳じゃないから[DEL]キーもとは言わないけどせめて[BS]
  ぐらいは機能させて欲しいね。

こういったのを関数にして作っていったらどうでしょう。
そうやって考えると今回の電卓?で必要な機能はどうなりますか?

 今例を上げた数字入力、完成された文字列を構文解析する関数、解析結果から
演算処理を行う関数とか考えればいろいろ出てきますよ。そういった設計仕様を
固めてみて一つづつ組み立ててみませんか?課題としては結構面白いと思うけど。

No.4030

Re:電卓
投稿者---silver fox(2002/12/23 13:43:05)


>できなさそうですね・・・−0−;

ここと
ここだってさ

No.4033

Re:電卓
投稿者---由兎(2002/12/24 01:18:46)


>ここと
>ここだってさ

調べてくれたんですね。ありがとうございます。
時間がないので今のところパッと見ただけですけど
難しそうですね。少しずつ理解しながら頑張ってみます。


No.4029

Re:電卓
投稿者---かずま(2002/12/23 10:44:40)


> というものを作ってみたのですが
> 例えば5+4*2=と入力すると本当なら13なのですが
> 18になってしまいます。
> どのようにしたら良いでしょうか?

= の後に改行が必要というのでよければ、次のように書けます。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

double expression(const char *s)
{
    double v;  static char c;

    if (*s) {
        for (v = expression(s+2); c == s[0] || c == s[1]; )
            switch (c) {
            case '+': v += expression(s+2); break;
            case '-': v -= expression(s+2); break;
            case '*': v *= expression(s+2); break;
            case '/': v /= expression(s+2); break;
            }
        if (strchr("+-*/=", c)) return v;
    } else if (scanf("%lf %c", &v, &c) == 2) return v;
    puts("error");
    exit(1);
}

int main(void)
{
    printf("  %.15g\n", expression("+-*/"));
    return 0;
}


No.4034

Re:電卓
投稿者---由兎(2002/12/24 01:21:21)


コンパイルして実行してみると、ちゃんと計算できました^^
けど・・・なぜそういう風になるのかが分かりません^^;;;;


No.4243

Re:電卓
投稿者---かずま(2003/01/08 02:03:20)


> = の後に改行が必要というのでよければ、次のように書けます。

このプログラムですが、if (strchr("+-*/=", c)) return v; の部分が
気に入らないので、次のように修正します。
#include <stdio.h>

char c;

double expression(const char *s)
{
    double v;

    if (*s)
        for (v = expression(s+2); c == s[0] || c == s[1]; )
            switch (c) {
            case '+': v += expression(s+2); break;
            case '-': v -= expression(s+2); break;
            case '*': v *= expression(s+2); break;
            case '/': v /= expression(s+2); break;
            }
    else if (scanf("%lf %c", &v, &c) != 2) v = c = 0;
    return v;
}

int main(void)
{
    double v = expression("+-*/");

    printf((c != '=') ? "  error\n" : "  %.15g\n", v);
    return 0;
}

なぜ、if (strchr("+-*/=", c)) return v; が気に入らないかというと、
まず、ここで、c が '*' や '/' になることはあり得ないからです。
では、if (strchr("+-=", c)) return v; にすれば済むように思えますが、
あとで、括弧が使えるように仕様を拡張しようとしたとき、これを
if (strchr("+-=)", c)) にしなければならないなど、やっかいなことが
発生するからです。