/* a little calculator. * typical usage: ./calc "12.5*cos(pi/4)+3.4" * all numbers are long double * knows +,-,*,/,cos,sin,tan,pi (maybe more, read the code) * tries to print "exact" value at the end if * 0 <= exp <= 64 where exp is exponent of result long double * works on ia32, amd64, probably not on big endian hosts, * maybe on other little endian ones... */ #include #include #include #include #include #include char *floating_part[64] = { "5000000000000000000000000000000000000000000000000000000000000000", "2500000000000000000000000000000000000000000000000000000000000000", "1250000000000000000000000000000000000000000000000000000000000000", "0625000000000000000000000000000000000000000000000000000000000000", "0312500000000000000000000000000000000000000000000000000000000000", "0156250000000000000000000000000000000000000000000000000000000000", "0078125000000000000000000000000000000000000000000000000000000000", "0039062500000000000000000000000000000000000000000000000000000000", "0019531250000000000000000000000000000000000000000000000000000000", "0009765625000000000000000000000000000000000000000000000000000000", "0004882812500000000000000000000000000000000000000000000000000000", "0002441406250000000000000000000000000000000000000000000000000000", "0001220703125000000000000000000000000000000000000000000000000000", "0000610351562500000000000000000000000000000000000000000000000000", "0000305175781250000000000000000000000000000000000000000000000000", "0000152587890625000000000000000000000000000000000000000000000000", "0000076293945312500000000000000000000000000000000000000000000000", "0000038146972656250000000000000000000000000000000000000000000000", "0000019073486328125000000000000000000000000000000000000000000000", "0000009536743164062500000000000000000000000000000000000000000000", "0000004768371582031250000000000000000000000000000000000000000000", "0000002384185791015625000000000000000000000000000000000000000000", "0000001192092895507812500000000000000000000000000000000000000000", "0000000596046447753906250000000000000000000000000000000000000000", "0000000298023223876953125000000000000000000000000000000000000000", "0000000149011611938476562500000000000000000000000000000000000000", "0000000074505805969238281250000000000000000000000000000000000000", "0000000037252902984619140625000000000000000000000000000000000000", "0000000018626451492309570312500000000000000000000000000000000000", "0000000009313225746154785156250000000000000000000000000000000000", "0000000004656612873077392578125000000000000000000000000000000000", "0000000002328306436538696289062500000000000000000000000000000000", "0000000001164153218269348144531250000000000000000000000000000000", "0000000000582076609134674072265625000000000000000000000000000000", "0000000000291038304567337036132812500000000000000000000000000000", "0000000000145519152283668518066406250000000000000000000000000000", "0000000000072759576141834259033203125000000000000000000000000000", "0000000000036379788070917129516601562500000000000000000000000000", "0000000000018189894035458564758300781250000000000000000000000000", "0000000000009094947017729282379150390625000000000000000000000000", "0000000000004547473508864641189575195312500000000000000000000000", "0000000000002273736754432320594787597656250000000000000000000000", "0000000000001136868377216160297393798828125000000000000000000000", "0000000000000568434188608080148696899414062500000000000000000000", "0000000000000284217094304040074348449707031250000000000000000000", "0000000000000142108547152020037174224853515625000000000000000000", "0000000000000071054273576010018587112426757812500000000000000000", "0000000000000035527136788005009293556213378906250000000000000000", "0000000000000017763568394002504646778106689453125000000000000000", "0000000000000008881784197001252323389053344726562500000000000000", "0000000000000004440892098500626161694526672363281250000000000000", "0000000000000002220446049250313080847263336181640625000000000000", "0000000000000001110223024625156540423631668090820312500000000000", "0000000000000000555111512312578270211815834045410156250000000000", "0000000000000000277555756156289135105907917022705078125000000000", "0000000000000000138777878078144567552953958511352539062500000000", "0000000000000000069388939039072283776476979255676269531250000000", "0000000000000000034694469519536141888238489627838134765625000000", "0000000000000000017347234759768070944119244813919067382812500000", "0000000000000000008673617379884035472059622406959533691406250000", "0000000000000000004336808689942017736029811203479766845703125000", "0000000000000000002168404344971008868014905601739883422851562500", "0000000000000000001084202172485504434007452800869941711425781250", "0000000000000000000542101086242752217003726400434970855712890625" }; void float_add(unsigned char *r, unsigned char *v) { int i; int ret=0; for (i=63; i>=0; i--) { int n = r[i] + v[i] - '0' + ret; if (n>9) ret = 1; else ret = 0; r[i] = n; if (ret) r[i] -= 10; } if (ret) { printf("impossible\n"); exit(1); } } unsigned char *get_as_float(long long v) { static unsigned char ret[65]; int i; for (i=0; i<64; i++) ret[i] = 0; v <<= 1; for (i=0; i<64; i++) { if (v & 0x8000000000000000LL) float_add(ret, (unsigned char *)floating_part[i]); v <<= 1; } for (i=0; i<64; i++) ret[i] += '0'; ret[64] = 0; for (i=63; i>=0; i--) if (ret[i] == '0') ret[i] = 0; else break; return ret; } #define ERR do{printf("%s:%d:ERR\n",__FUNCTION__,__LINE__);exit(1);}while(0) unsigned char s[65536]; int p; int get_char(void) { if (s[p] == 0) return -1; p++; return s[p-1]; } void unget_char(int c) { if (c == -1) return; if (p == 0) ERR; p--; } enum token { TEOF, NUMBER, MUL, DIV, ADD, SUB, PAR_OPEN, PAR_CLOSE, COS, SIN, TAN, LOG10, EXP }; typedef struct { enum token type; long double val; } tok; int has_wait_tok; tok wait_tok; void remove_space(void) { int c; do c = get_char(); while (isspace(c)); unget_char(c); } char id[512]; int id_pos; void add_id(char c) { if (id_pos == 512) ERR; id[id_pos] = c; id_pos++; } void reset_id(void) { id_pos = 0; } tok _get_token(void) { int c; tok t; long double v; if (has_wait_tok) { has_wait_tok = 0; return wait_tok; } remove_space(); c = get_char(); switch (c) { case -1: t.type = TEOF; break; case '*': t.type = MUL; break; case '/': t.type = DIV; break; case '+': t.type = ADD; break; case '-': t.type = SUB; break; case '^': t.type = EXP; break; case '0'...'9': goto number; case '(': t.type = PAR_OPEN; break; case ')': t.type = PAR_CLOSE; break; default: goto identifier; } return t; number: t.type = NUMBER; t.val = c - '0'; while (1) { c = get_char(); if (!(c >= '0' && c <= '9')) break; t.val *= (long double)10; t.val += (long double)(c - '0'); } unget_char(c); /* dot? */ c = get_char(); if (c != '.') { unget_char(c); return t; } v = (long double)1/(long double)10; while (1) { c = get_char(); if (!(c >= '0' && c <= '9')) break; t.val += v * (long double)(c-'0'); v /= (long double)10.; } unget_char(c); return t; identifier: reset_id(); while (isalnum(c)) { add_id(c); c = get_char(); if (c == -1) break; } unget_char(c); add_id(0); if (!strcmp(id, "pi")) { t.type = NUMBER; t.val = (long double)4 * atanl(1); return t; } //printf("'%s'\n", id); if (!strcmp(id, "cos")) { t.type = COS; return t; } if (!strcmp(id, "sin")) { t.type = SIN; return t; } if (!strcmp(id, "tan")) { t.type = TAN; return t; } if (!strcmp(id, "log10")) { t.type = LOG10; return t; } /* unknown id */ ERR; } tok get_token(void) { tok t; t = _get_token(); return t; } void unget_token(tok t) { has_wait_tok = 1; wait_tok = t; } long double calc(void); long double base(void) { tok t; t = get_token(); if (t.type == NUMBER) return t.val; if (t.type == TEOF) ERR; if (t.type == SUB) { long double v = base(); return -v; } if (t.type == PAR_OPEN) { long double v = calc(); t = get_token(); if (t.type != PAR_CLOSE) ERR; return v; } if (t.type == COS || t.type == SIN || t.type == TAN || t.type == LOG10) { long double v; int type = t.type; t = get_token(); if (t.type != PAR_OPEN) ERR; v = calc(); t = get_token(); if (t.type != PAR_CLOSE) ERR; switch (type) { case COS: return cosl(v); case SIN: return sinl(v); case TAN: return tanl(v); case LOG10: return log10l(v); } } ERR; } long double _exp(void) { long double v, v2; tok op; v = base(); while (1) { op = get_token(); if (op.type == TEOF) return v; if (op.type != EXP) { unget_token(op); return v; } v2 = base(); v = powl(v, v2); } } long double mul(void) { long double v, v2; tok op; v = _exp(); while (1) { op = get_token(); if (op.type == TEOF) return v; if (op.type != MUL && op.type != DIV) { unget_token(op); return v; } v2 = _exp(); if (op.type == MUL) v *= v2; else v /= v2; } } long double add(void) { long double v, v2; tok op; v = mul(); while (1) { op = get_token(); if (op.type == TEOF) return v; if (op.type != ADD && op.type != SUB) { unget_token(op); return v; } v2 = mul(); if (op.type == ADD) v += v2; else v -= v2; } } long double calc(void) { return add(); } void output(long double v) { unsigned char *t = (unsigned char *)&v; unsigned long long x = ((unsigned long long)t[7] << (24+32)) | ((unsigned long long)t[6] << (24+24)) | ((unsigned long long)t[5] << (24+16)) | ((unsigned long long)t[4] << (24+8)) | ((unsigned long long)t[3] << 24) | ((unsigned long long)t[2] << 16) | ((unsigned long long)t[1] << 8) | ((unsigned long long)t[0]); int exp = (((t[9]&127) << 8) | t[8]) - 16383; int sign = t[9] & 128; if (exp >= 0 && exp <= 63) { unsigned long long it = x >> (63-exp); unsigned long long fp = (((0x7fffffffffffffffLL) >> exp) & x) << exp; unsigned char *f = get_as_float(fp); if (sign) printf("-"); printf("%llu", it); if (f[0]) printf(".%s", f); printf("\nwith %%Lg:"); } printf("%Lg\n", v); printf("raw:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9]); } void go(void) { tok last; output(calc()); last = get_token(); if (last.type != TEOF) printf("WARNING: your expr was not fully parsed. Result may be wrong.\n"); } int main(int n, char **v) { int i; int l; if (n == 1) { printf("gimme something to calc\n"); return 1; } l=0; for (i=1; i 65535) { printf("command line too long\n"); return 1; } l = 0; for (i = 1; i < n; i++) { strcat((char *)s+l, v[i]); l += strlen(v[i]); } go(); return 0; }