One of the signs that you love Python is when you start to use it as a simple calculator. The problem with that is beyond the usefulness of 'sum' the interactive interpreter is not optimal for any calculations beyond a few numbers. This mostly seems to stem from the numbers not being formatted in a nice fashion; 2345634+2894756-2345823
is not the easiest thing to read. That's where an accountant's calculator comes in handy; the tape presents numbers in a column view that is very uncluttered. And thanks to the decimal package a very simple one can be implemented quickly.
To use this recipe you input the number, an optional space, and then the operator (/, *, -, or +; everything you would find on the numeric keypad on your keyboard) and then press return. This will apply the number to the running total using the operator. To output the total just enter a blank line. To quit enter the letter 'q' and press return. This simple interface matches the output of a typical accountant's calculator, removing the need to have some other form of output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | """A *very* simple command-line accountant's calculator.
Uses the decimal package (introduced in Python 2.4) for calculation accuracy.
Input should be a number (decimal point is optional), an optional space, and an
operator (one of /, *, -, or +). A blank line will output the total.
Inputting just 'q' will output the total and quit the program.
"""
import decimal
import re
parse_input = re.compile(r'(?P<num_text>\d*(\.\d*)?)\s*(?P<op>[/*\-+])')
total = decimal.Decimal('0')
total_line = lambda val: ''.join(('=' * 5, '\n', str(val)))
while True:
tape_line = raw_input()
if not tape_line:
print total_line(total)
continue
elif tape_line is 'q':
print total_line(total)
break
try:
num_text, space, op = parse_input.match(tape_line).groups()
except AttributeError:
raise ValueError("invalid input")
num = decimal.Decimal(num_text)
if op is '/':
total /= num
elif op is '*':
total *= num
elif op is '-':
total -= num
elif op is '+':
total += num
else:
raise ValueError("unsupported operator: %s" % op)
|
This recipe is only possible thanks to the decimal package introduced in Python 2.4 . It allows for very high-precision decimal arithmetic that is just not possible using binary floating point numbers; no more lost pennies thanks to binary floating point not being able to represent exact values.
A nice improvement would be to remove the need to press return after each operator. That would require having stdin be unbuffered and reading in every character to detect when it is entered. Also a GUI version that visually looked like a calculator would be nice.
Improvements - no return needed, clear function, arbitrary decimal places. I was very pleased with this recipe, but wanted it to do a little more. Here is my version, which only works on windows; you'll need to override getchar for other operating systems. See Recipe 134892 for more on how to do that.