Code:
// calc.cpp
// a four-function calculator
// author: *ClownPimp*
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
//#include <float.h> dammit I couldnt get it to work
#include <string.h>
struct MATH_ERROR {
const char *msg;
MATH_ERROR(const char *message)
:msg(message)
{}
};
struct PARTS{
int sign:1;
int exp:8;
int base:23;
};
union FLOAT{
float fl;
PARTS parts;
};
void calc_func(char *input, int &total);
int parseInput(const char *input, char &op, int &val);
bool isSupportedOperator(char c);
void swap(int &, int &);
int add(int, int);
int sub(int, int);
int mul(int, int);
int divide(int, int); // div(int, int) already defined in stdlib
int overflow(int, int, int);
// i dont know why, but I love macros
#define isnegative(x) ((x) >> (sizeof(x)*8-1)) // sizeof(x)*8-1 is a constant
#define abs(x) ((x) < (0)? add(1, ~x): (x))
void calc_func(char *input, int &total)
{
char op;
int val;
// ive never used a try/catch block before, just thought
// id give it a try
try {
if (parseInput(input, op, val))
puts("invalid input");
else {
switch(op) {
case '=':
total = val;
break;
case '+':
total = add(total, val);
break;
case '-':
total = sub(total, val);
break;
case '*':
total = mul(total, val);
break;
case '/':
total = divide(total, val);
break;
}
}
}
catch (MATH_ERROR me) {
printf("error: %s\n", me.msg);
}
}
int parseInput(const char *input, char &op, int &val)
{
bool got_op = false, got_val = false;
while (*input) {
if (isSupportedOperator(*input) && !got_op) {
op = *input;
got_op = true;
}
else if (isdigit(*input) || (got_op && *input == '-')) {
val = atoi(input);
got_val = true;
break;
}
++input;
}
return (got_op == true && got_val == true) ? 0: -1;
}
bool isSupportedOperator(char c)
{
return c == '*' || c == '/' || c == '+' || c == '-' || c == '=';
}
int overflow(int A, int B, int result)
{
// if A and B are of the same sign and the result is not
// of the same sign of A and B, then overflow occured.
// (ie if 100 + 100 = -something, then we know overflow occured)
return (isnegative(A) == isnegative(B)) &&
(isnegative(A) == !isnegative(result));
}
void swap(int &x, int &y)
{
int t = x;
x = y;
y = t;
}
int add(int A, int B)
{
int carry = 0, result = 0, mask = 1;
for (int i = 0; i < sizeof(int)*8; ++i) {
result |= (mask << i) & (A ^ B ^ carry);
carry = ((A ^ B) & carry) | (A & B); // dont bother masking the other bits
carry <<= 1; // because we dont care about them
}
if (overflow(A, B, result))
throw MATH_ERROR("overflow");
return result;
}
int sub(int A, int B)
{
// didnt know if i could do -A or even ~A+1 here
return add(A, add(1, ~B));
}
int mul(int A, int B)
{
int result = 0;
// make sure we are adding the larger value
if (B > A)
swap(A, B);
for (int i = 0; i < B; ++i)
result = add(A, result);
// check if input had opposite signs
if (isnegative(A) != isnegative(B))
return add(1, ~result);
return result;
}
int divide(int A, int B)
{
int a, b, c, high, low, middle, overflow = false;
if (B == 0)
throw MATH_ERROR("divide by zero");
a = abs(A);
b = abs(B);
high = a;
low = 1;
if (b > a)
return 0;
do {
// low + (high-low)/2 instead of (high+low)/2
// necessary to avoid overflow with large numbers
middle = add(low, sub(high, low) >> 1);
try {
c = mul(middle, b);
}
catch(MATH_ERROR me) {
overflow = 1;
}
if (overflow || c > a) {// if overflow occured we know 'c' was too high
overflow = false;
high = middle;
}
else
low = middle;
}while (!(a >= c && c > a-b));
// check if input has opposite signs
if (isnegative(A) != isnegative(B))
return add(1, ~middle);
return middle;
}
int main()
{
int total = 0;
char buf[100];
gets(buf);
while (buf[0] != 'q') {
calc_func(buf, total);
printf("total: %d\n", total);
gets(buf);
}
return 0;
}