質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

745閲覧

[C] 入力数値が不安定

Gurt

総合スコア14

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2020/08/06 05:20

前提・実現したいこと

逆ポーランド記法で入力された数式の計算を行いたいと考えています。
また、制約としてスタックを用いて実装し、そのスタックは連結リストを用いて実装するというものです。
試作段階としてスタックのみを使用して実装した逆ポーランド記法の計算は問題なく計算できるのですが、スタックを連結リストで実装した際に、入力した数値が小数点以下6位や7位あたりで少しずれてしまい、計算結果が大きく変わってしまいました。数値の変動はGDBで確認しました。
入力方法として逆ポーランド記法の数式を文字列として入力しました。その際、数値と演算子の後にはスペースを入れています。これはスペースまでを一区切りにし、演算子だった場合はその演算子の演算を行い、数値だった場合はatofで数値へ戻しています。

これは、単純にプログラムが間違っているのかC言語やPCの特性なのかそのほかの要因なのか教えていただきたいと考えています。

該当のソースコード

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <stdbool.h> 4 5#define STACK_SIZE 8 6#define DATA_SIZE 64 7 8struct cell{ 9 float value; 10 struct cell *next; 11}; 12 13struct cell *head = NULL; 14struct cell **p; 15 16bool first_flag = false; 17 18int PostfixNotation(char pnData_c[STACK_SIZE]); 19int Push(float data); 20int Pop(float *data, int n); 21void InsertCell(struct cell **p_pointer, float new_value); 22float DeleteCell(struct cell **p_pointer, int n); 23 24int main(){ 25 char pnData[64]; 26 float ans = 0; 27 int n = 0; 28 29 printf("input data(input space after number and operator)\n"); 30 scanf("%[^\n]%*c", &pnData); 31 32 PostfixNotation(pnData); 33 34 Pop(&ans, n - 1); 35 printf("ans : %.2f\n", ans); 36 37 return 0; 38} 39 40int PostfixNotation(char pnData_c[STACK_SIZE]){ 41 char temp[8] = ""; 42 int i = 0, j = 0, n = 0; 43 float pnData_f, first = 0, second = 0; 44 while(pnData_c[i] != '\0'){ 45 if(pnData_c[i] == ' '){ 46 if(pnData_c[i - 1] == '+'){ 47 Pop(&second, n - 1); 48 n--; 49 Pop(&first, n - 1); 50 n--; 51 Push(first + second); 52 }else if(pnData_c[i - 1] == '-'){ 53 Pop(&second, n - 1); 54 n--; 55 Pop(&first, n - 1); 56 n--; 57 Push(first - second); 58 n++; 59 }else if(pnData_c[i - 1] == '*'){ 60 Pop(&second, n - 1); 61 n--; 62 Pop(&first, n - 1); 63 n--; 64 Push(first * second); 65 n++; 66 }else if(pnData_c[i - 1] == '/'){ 67 Pop(&second, n - 1); 68 n--; 69 Pop(&first, n - 1); 70 n--; 71 Push(first / second); 72 n++; 73 }else{ 74 pnData_f = atof(temp); 75 Push(pnData_f); 76 n++; 77 } 78 j = 0; 79 i++; 80 for(int k = 0; k < STACK_SIZE; k++){ 81 temp[k] = '\0'; 82 } 83 } 84 temp[j] = pnData_c[i]; 85 j++; 86 i++; 87 } 88} 89 90int Push(float data){ 91 if(!first_flag){ 92 p = &head; 93 first_flag = true; 94 } 95 InsertCell(p, data); 96 p = &((*p)->next); 97 return 1; 98} 99 100int Pop(float *data, int i){ 101 p = &head; 102 *data = DeleteCell(p, i - 1); 103 return 1; 104} 105 106void InsertCell(struct cell **p_pointer, float new_value){ 107 struct cell* new_cell; 108 new_cell = malloc(sizeof(struct cell)); 109 new_cell->value = new_value; 110 new_cell->next = *p_pointer; 111 *p_pointer = new_cell; 112} 113 114float DeleteCell(struct cell **p_pointer, int i){ 115 struct cell *delete_cell; 116 float pop_data = 0; 117 while(i > 0 && *p_pointer != NULL){ 118 p_pointer = &((*p_pointer)->next); 119 i--; 120 } 121 delete_cell = *p_pointer; 122 pop_data = delete_cell->value; 123 *p_pointer = delete_cell->next; 124 free((void *)delete_cell); 125 return pop_data; 126}

入力例として
(1.7 + 2.8) * (2.5 - 4.7) + (-1)
を計算する場合は
1.7 2.8 + 2.5 4.7 - * -0.1 +
を入力しています。(最後の+の後にもスペースが入っています)

試作段階のスタックのみを使用したプログラム

c

1#include <stdio.h> 2#include <stdlib.h> 3 4#define STACK_SIZE 8 5 6float stack[STACK_SIZE]; 7int sp = 0; 8 9int Push(float data); 10int Pop(float *data); 11int PostfixNotation(char pnData_c[STACK_SIZE]); 12 13int main(){ 14 char pnData[64]; 15 float ans = 0; 16 17 printf("input data(input space after number and operator)\n"); 18 scanf("%[^\n]%*c", &pnData); 19 20 PostfixNotation(pnData); 21 22 Pop(&ans); 23 printf("ans : %.2f\n", ans); 24 25 return 0; 26} 27 28int Push(float data){ 29 if(sp >= STACK_SIZE){ 30 return 0; 31 }else{ 32 stack[sp] = data; 33 sp++; 34 return 1; 35 } 36} 37 38int Pop(float *data){ 39 if(sp <= 0){ 40 return 0; 41 }else{ 42 sp--; 43 *data = stack[sp]; 44 return 1; 45 } 46} 47 48int PostfixNotation(char pnData_c[STACK_SIZE]){ 49 char temp[8] = ""; 50 int i = 0, j = 0; 51 float pnData_f, first = 0, second = 0; 52 while(pnData_c[i] != '\0'){ 53 if(pnData_c[i] == ' '){ 54 if(pnData_c[i - 1] == '+'){ 55 Pop(&second); 56 Pop(&first); 57 Push(first + second); 58 }else if(pnData_c[i - 1] == '-'){ 59 Pop(&second); 60 Pop(&first); 61 Push(first - second); 62 }else if(pnData_c[i - 1] == '*'){ 63 Pop(&second); 64 Pop(&first); 65 Push(first * second); 66 }else if(pnData_c[i - 1] == '/'){ 67 Pop(&second); 68 Pop(&first); 69 Push(first / second); 70 }else{ 71 pnData_f = atof(temp); 72 Push(pnData_f); 73 } 74 j = 0; 75 i++; 76 for(int k = 0; k < STACK_SIZE; k++){ 77 temp[k] = '\0'; 78 } 79 } 80 temp[j] = pnData_c[i]; 81 j++; 82 i++; 83 } 84}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

ええと、根本的な原因は、連結リスト版でスタックの実装をミスっていることです。
1 2 - 」とか「1 2 / 」とかを試してみれば分かりますが、LIFO(FILO)が実現できていません。

連結リストを使ったスタックは、提示されているコードよりももっと単純に書けます。

Push: headに新たな値を持ったセルを挿入する(現在のheadのセルがnextになる)。
Pop: headのセルの値を取り出し、そのセルを削除する(nextのセルがheadになる)。

これだけです。連結リスト内を走査する必要はありません。

投稿2020/08/06 05:50

編集2020/08/06 05:51
Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

DreamTheater

2020/08/06 05:57

横から失礼します。 float型見て安易に丸め誤差と回答した自分が恥ずかしいです。 コードをちゃんと読まないとダメですね。
Gurt

2020/08/06 09:49

スタックのpush,popはそのように実装することができるのですね。 確かに詳しく調べてみると計算の順序が関係ない加算乗算はできていましたが、減算除算ができていませんでした。 参考にした教本では、pushは配列にただ追加して配列の番号をポインタのようにpopで参照するというものでした。 そのため連結リストの挿入と削除においてイメージの差異が生まれたのだと思います。 もう一度やり直してみます。 完全に解決したらBAにさせていただきます。
Gurt

2020/08/07 20:38

すみません、お待たせいたしました。 無事に解決できました。
guest

0

float使っているので浮動小数点の丸め誤差が発生したのでは?
doubleにすれば少し精度が高くなりますが、、、

以下をご覧ください
FLP02-C. 精度の高い計算が必要な場合は浮動小数点数の使用を避ける
C言語 計算の誤差

投稿2020/08/06 05:28

DreamTheater

総合スコア1095

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Gurt

2020/08/06 05:46

連結リストを使う前はたまたまfloatでも計算できていたということなのですかね・・・ doubleにしても計算結果変わらずでした。 ただ、GDBでみた数値はfloatよりはブレ幅が小さいように感じました。 他に何か要因があるのでしょうか・・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問