【適用】浮動小数点数四則演算器Part 3:演算モジュールの作成

68759 ワード

【適用】浮動小数点数四則演算器Part 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); } } // ( )