【適用】浮動小数点数四則演算器Part 3:演算モジュールの作成
68759 ワード
【適用】浮動小数点数四則演算器Part 3:演算モジュールの作成
既知:接尾辞式はすでに1つのスタックに規則的に配置されています.
接尾辞式の計算は主に次のように行われます.
1.データを保存するスタックをfx 1とし、新しいスタックansを作成する.
2.毎回読み込む時、fx 1が空でない場合:
1)データの場合は、直接スタックに入ります.
2)オペレータの場合:
3.ansのスタックトップ要素を出力します.
注意!ansの要素の個数が1でないと、問題が発生し、extra code 11:BUG SHOWEDを出力します!DAMN!!!
出力数が浮動小数点数であるか否かを1000倍精度で判断して出力することもできる.
コード:
前の3つの文章のモジュールをつなぎ合わせると、完全なプログラムが得られます.
しかし、プログラムの使用者に一定の助けが必要であるため、次の指導モジュールがあります.
作業終了後の助教君からの参考用標準手順も下に折りたたみます.
既知:接尾辞式はすでに1つのスタックに規則的に配置されています.
接尾辞式の計算は主に次のように行われます.
1.データを保存するスタックをfx 1とし、新しいスタックansを作成する.
2.毎回読み込む時、fx 1が空でない場合:
1)データの場合は、直接スタックに入ります.
2)オペレータの場合:
(1) '/': ans , 。
(2) '/': ans , 0, error code 10:'0' under '/', 。
3.ansのスタックトップ要素を出力します.
注意!ansの要素の個数が1でないと、問題が発生し、extra code 11:BUG SHOWEDを出力します!DAMN!!!
出力数が浮動小数点数であるか否かを1000倍精度で判断して出力することもできる.
stack<double> ans;
void Out(double i){
double a;
int a0,a1;
a=i;
a0=1000*a;
if (a0%1000==0){
a1=a;
printf("out>>%d
",a1);
}
else{
printf("out>>%.2lf
",a);
}
}
コード:
void cal(){
Error=0;
if (ans.empty()==0){
ans.pop();
}
double a=0.0,b=0.0;
char tmp[300];
while (fx1.empty()==0){
if (fx1.top()=="+"||
fx1.top()=="-"||
fx1.top()=="*"){
a=ans.top();
ans.pop();
b=ans.top();
ans.pop();
if (fx1.top()=="+"){ans.push(b+a);}
if (fx1.top()=="-"){ans.push(b-a);}
if (fx1.top()=="*"){ans.push(b*a);}
fx1.pop();
continue;
}
if (fx1.top()=="/"){
if (ans.top()==0){
Error=1;
printf("out>>error code 10: '0' under '/'
");
return ;
}
a=ans.top();
ans.pop();
b=ans.top();
ans.pop();
ans.push(b/a);
fx1.pop();
continue;
}
for (int i=0;i<300;i++){
tmp[i]=0;
}
strcpy(tmp,fx1.top().c_str());
ans.push(atof(tmp));
fx1.pop();
}
if (ans.size()!=1){
printf("out>>error code 11: BUG SHOWED!DAMN!!!
");
}
else{
Out(ans.top());
ans.pop();
}
}
前の3つの文章のモジュールをつなぎ合わせると、完全なプログラムが得られます.
しかし、プログラムの使用者に一定の助けが必要であるため、次の指導モジュールがあります.
void guide(){
printf(" :
");
printf(" , 。
");
printf(" , 。
");
printf(" , 。
");
printf(" , :
");
printf("in>>x y = 3.1
");
printf("out>>3.1
");
printf("in>>( x + y ) * -3
");
printf("out>>-18.60
");
printf(" 。");
printf(" exit 。
");
printf("===============================================
");
}
作業終了後の助教君からの参考用標準手順も下に折りたたみます.
#include
#include
#include
#include
#include
#include
using namespace std;
// debug flag
#define $ if(0)
const int MAX_VAR_LENGTH=107;
const char POSITIVE=1,NEGATIVE=2,END=3; // special operators
int precedence[256]; // char -> precedence
struct Number { // to deal with auto float/int recognition
double val;
bool is_integer;
};
// variable stuffs
string var_name;
inline bool valid_var_char(char c) {
return (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') || c=='_';
}
inline int read_var_name(char *s) {
char var_name_tmp[MAX_VAR_LENGTH];
if(!valid_var_char(s[0]) || (s[0]>='0'&&s[0]<='9'))
return 0;
var_name_tmp[0]=s[0];
int i=1;
for(;valid_var_char(s[i]);i++)
var_name_tmp[i]=s[i];
var_name_tmp[i]='\0';
var_name=var_name_tmp;
return i;
}
map<string,Number> var_store;
// apply `op` to `val`s
inline void apply(stack<Number> &val,char op) {
if(op=='(' || op==')' || op==END) return;
if(val.empty()) throw "no sufficient operand";
Number v2=val.top(); val.pop();
// unary operators
if(op==POSITIVE) {
val.push(v2);
return;
} else if(op==NEGATIVE) {
val.push(Number{-v2.val,v2.is_integer});
return;
}
if(val.empty()) throw "no sufficient operand";
Number v1=val.top(); val.pop();
// binary operators
Number res;
res.is_integer=v1.is_integer&&v2.is_integer;
if(op=='+') res.val=v1.val+v2.val;
else if(op=='-') res.val=v1.val-v2.val;
else if(op=='*') res.val=v1.val*v2.val;
else if(op=='/') {
if(fabs(v2.val)<1e-6) throw "division by zero";
res.val=v1.val/v2.val;
}
else throw "invalid operator";
// round the result if is integer
if(res.is_integer) res.val=double(int(res.val));
$ printf("[APPLY] %lf %c %lf = %lf
",v1.val,op,v2.val,res.val);
val.push(res);
}
// calc a pure expression
inline Number calc(char *s) {
int len=(int)strlen(s);
s[len++]=END;
stack<char> op;
op.push(END);
stack<Number> val;
bool last_is_operand=false;
for(int i=0;i<len;) {
if(s[i]==' ') {
$ printf("[SPACE] at %d
",i);
i++;
} else if(int var_len=read_var_name(s+i)) { // variable
if(last_is_operand) throw "continuous operand";
if(var_store.find(var_name)==var_store.end()) throw "uninitialized variable";
$ printf("[VAR] at %d: %s
",i,var_name.c_str());
val.push(var_store[var_name]);
last_is_operand=true;
i+=var_len;
} else if((s[i]>='0'&&s[i]<='9')||s[i]=='.') { // number literal
if(last_is_operand) throw "continuous operand";;
double curval;
int curlen;
sscanf(s+i,"%lf%n",&curval,&curlen);
$ printf("[VAL] at %d+%d: %lf
",i,curlen,curval);
bool is_integer=true;
for(int j=i;j<i+curlen;j++)
if(s[j]=='.')
is_integer=false;
if(curlen==0 || (!is_integer && curlen==1)) // '.'
throw "invalid number";
val.push(Number{curval,is_integer});
last_is_operand=true;
i+=curlen;
} else { // operator
char o=s[i];
// detect unary POSITIVE & NEGATIVE
int prev=i-1;
while(prev>=0 && s[prev]==' ') prev--;
if((o=='+' || o=='-') && ( // we use s[prev] instead of s[i-1] here because of possible whitespaces
prev==-1 || (s[prev]=='(' || s[prev]=='+' || s[prev]=='-' || s[prev]=='*' || s[prev]=='/')
)) {
o=(o=='+')?POSITIVE:NEGATIVE;
}
$ printf("[OP] at %d: %c
",i,o);
if(!precedence[(int)o]) {
throw "unknown operator";
}
if(o=='(') {
if(last_is_operand) throw "invalid usage of bracket";
op.push(o);
} else if(o==')') {
if(!last_is_operand) throw "invalid usage of bracket";
while(!op.empty() && op.top()!='(') {
char top=op.top();
$ printf("[APPLY] %c : %c
",o,top);
apply(val,top); op.pop();
}
if(op.empty()) throw "invalid usage of bracket";
$ printf("[APPLY] %c : (
",o);
op.pop(); //'('
} else { // normal arithmetic operator
if(o!=POSITIVE && o!=NEGATIVE) // unary operator should not apply right now, or -+2 will cause an error
while(!op.empty() && precedence[(int)o]<=precedence[(int)op.top()]) {
char top=op.top();
$ printf("[APPLY] %c : %c
",o,top);
apply(val,top); op.pop();
}
op.push(o);
last_is_operand=false;
}
i++;
}
}
// finished
if(op.size()!=1 || val.size()!=1)
throw "bad expression";
return val.top();
}
int main() {
char s[10007];
precedence[(int)'(']=1, precedence[(int)')']=1,
precedence[(int)END]=2, // precedence of END is higher than ( and ) so we can check bracket errors correctly
precedence[(int)'+']=3, precedence[(int)'-']=3,
precedence[(int)'*']=4, precedence[(int)'/']=4,
precedence[(int)POSITIVE]=5, precedence[(int)NEGATIVE]=5;
while(true) {
Number res;
scanf("%*[ ]"); // ignore leading spaces
gets(s);
if(strcmp(s,"exit")==0)
return 0;
int len=(int)strlen(s),eqpos=-1;
for(int i=len-1;i>0;i--) // eqpos: position of the last `=`
if(s[i]=='=') {
eqpos=i;
break;
}
try {
if(eqpos==-1) // pure EXPRESSION
res=calc(s);
else { // VAR = EXPRESSION
res=calc(s+eqpos+1); // the EXPRESSION part
int name_len=read_var_name(s);
for(int i=name_len;i<eqpos;i++) // tackle with trailing spaces after variable name
if(s[i]!=' ')
throw "bad variable name";
// finally store it
$ printf("STORE TO %s
",var_name.c_str());
var_store[var_name]=res;
}
} catch(const char *e) {
printf("error: %s
",e);
//printf("error
");
continue;
}
// no error
printf(res.is_integer ? "%.0lf
" : "%.2lf
", res.val);
}
}
// ( )