# Programmer's calculator
( https://yurichev.com/progcalc )
Written by dennis(@)yurichev.com
## Why?
Just tired enough to fire up Mathematica each time for simple tasks, while UNIX bc calculator and IDA hasn't features I need.
It's no harder than calculators described in many lex/flex/yacc/bison tutorials.
## Usage
### Basic interaction
Input: `(1+10)*18`
Output:
(unsigned) 198 0xc6 0b11000110
Negative result is possible:
Input: `-123`
Output:
(unsigned) 18446744073709551493 0xffffffffffffff85 0b1111111111111111111111111111111111111111111111111111111110000101
(signed) -123
This means, -123 can be treated as both unsigned and signed and the calculator shows both versions.
Input value is treated as signed 64-bit integer, so you can enter -x, but you can't enter `0xffffffffffffffff` (`2^64-1`), but you can use `~0` for that.
Sometimes it's useful to know that a value can be represented as ASCII code.
Input: `12*3`
Output:
(unsigned) 36 0x24 0b100100 ASCII: '$'
Hexadecimal numbers can be inputted as in C/C++:
Input: `0x123`
Output:
(unsigned) 291 0x123 0b100100011
Binary numbers prefixed with `0b`.
Input: `0b101010`
Output:
(unsigned) 42 0x2a 0b101010 ASCII: '*'
Sometimes it's desirable to know that result of division is not integer:
Input: `10/3`
Output:
Warning, result is not integer: 3.333333; remainder: 10 % 3 = 1
(unsigned) 3 0x3 0b11
#### GNU Readline
... is used. So you can cycle over your last inputs using up/down arrows.
TAB autocompletion is also supported.
#### Exit
For exit, press Ctrl-C.
### Operations
Unary operations: `-` (negation) and `~` (boolean NOT).
Binary operations: `+` `-` `*` `/` `%` (remainder) `|` `^` `&` `<<` `>>` (unsigned shift right) `>>s` (signed shift right)
`**` is integer power (like in Algol): `2**16` is 0x10000, etc.
Order of operations is the same as in C/C++[1]:
* unary operations: `-` `~`
* `**` (power)
* `*` `/` `%`
* `-` `+`
* `<<` `>>` `>>s`
* `&`
* `^`
* `|`
### Functions and procedures
There are also functions, like these:
Input: `ror64(1,1)`
Output:
(unsigned) 9223372036854775808 0x8000000000000000 0b1000000000000000000000000000000000000000000000000000000000000000
(signed) -9223372036854775808
Others are: ror64(), ror32(), ror16(), ror8(), rol64(), rol32(), rol16(), rol8()
Another important function is `slb(...)` (Set Leading Bits (to 1)).
For example, you see signed 16-bit value `0xff70`. How to convert this number to signed decimal? The calculator works with unsigned 64-bit values internally.
So first you need to complement this value by all bits set at left:
Input: `slb(0xff70)`
Output:
(unsigned) 18446744073709551472 0xffffffffffffff70 0b1111111111111111111111111111111111111111111111111111111101110000
(signed) -144
Now we see this is -144.
Signed shift (>>s) may be important while working with negative signed numbers.
(The ">>s" notion has been taken from the "aha!" superoptimizer by Henry Warren[4].)
Let's divide value we've got by 4:
Input: `slb(0xff70) >>s 2`
Output:
(unsigned) 18446744073709551580 0xffffffffffffffdc 0b1111111111111111111111111111111111111111111111111111111111011100
(signed) -36
Effect of `slb(0xff70) >> 2` (unsigned shift right) will be obviously different.
There is also a function (or procedure) for calculating modulo inverse.
In RE4B book[2] you may find this example:
_a$ = 8 ; size = 4
_f PROC
mov ecx, DWORD PTR _a$[esp-4]
mov eax, 954437177 ; 38e38e39H
imul ecx
sar edx, 1
mov eax, edx
shr eax, 31 ; 0000001fH
add eax, edx
ret 0
_f ENDP
Let's find modulo inverse for 38e38e39H:
Input: `modinv32(0x38e38e39)`
Output:
Warning, result is not integer: 2^32/input=4.500000
Solving the equation 954437177x = 1+y*2^32
x=9, y=2
(unsigned) 9 0x9 0b1001
The problem is solved by two ways, first is calculating 2^32/input.
The result is 4.5, and then, addition shift right (`sar`) divides the whole result by 2 again.
So the function above divides input value by 9.
Solution for the equation acknowledges this.
`modinv64()` procedure is also present.
`log2' is logarithm base 2 (binary logarithm) of the result. It may be useful to know how many effective bits results has:
[1] 15*15
[1] (unsigned) 225 0xe1 0b11100001
[2] log2(%)
[2] (unsigned) 7 0x7 0b111
In our case there are 8 bits, so log2 of result is 7 (just add 1 to log2 value to get number of bits).
8 effective bits means this value can fit in a byte.
Read more about logarithms in M4P book[3].
Sometimes, in a compiled code, you see a float or double constant encoded in IEEE 754 format and you see just hexadecimal representation.
How to convert it to floating point number?
Input: `as_float(0x3FA66666)`
Output:
1.300000
Input: `as_float(0x3FBF5C29)`
Output:
1.495000
`as_double` procedure is also available.
For example, you may encounter this in a code:
mulsd xmm0, QWORD PTR .LC0[rip]
...
.LC0:
.long 1413754136
.long 1074340347
What does these numbers mean?
Input: `as_double(1074340347 << 32 | 1413754136)`
Output:
3.141593
Oh, now we see it's a Pi.
Sometimes you may also need to see how a floating point number is represented internally:
Input: `dump_ieee754(1.234)`
Output:
as 64-bit double:
0x3ff3be76c0000000
sign: 0 exponent: 0x3ff fraction:0x3be76c0000000
as 32-bit float:
0x3f9df3b6
sign: 0 exponent: 0x7f fraction:0x1df3b6
### Previous results
A last result can be used. It's, however, stored as a number, not as an expression:
[1] 123+456
[1] (unsigned) 579 0x243 0b1001000011
[2] %*3
[2] (unsigned) 1737 0x6c9 0b11011001001
[3] %*3
[3] (unsigned) 5211 0x145b 0b1010001011011
[4] %*3
[4] (unsigned) 15633 0x3d11 0b11110100010001
[5] %*3
[5] (unsigned) 46899 0xb733 0b1011011100110011
You may want to reuse previous results. %1 means 1st one, %2 - 2nd, etc:
[1] 2*2
[1] (unsigned) 4 0x4 0b100
[2] 4+7
[2] (unsigned) 11 0xb 0b1011
[3] 1+1
[3] (unsigned) 2 0x2 0b10
[4] %1+%2
[4] (unsigned) 15 0xf 0b1111
The syntax has been inspired by Mathematica[5].
### Command-line
Also possible:
% ./progcalc "2+2"
Programmer's calculator ...
(unsigned) 4 0x4 0b100
## Build
Linux: you need flex/bison installed.
Cygwin: required packages: make, gcc-core, flex, bison, libreadline-devel.
Run `make`.
## Issues
Cygwin version when run outside Cygwin command prompt, has issues due to readline library (try Up/Down arrows...)
## Download
https://yurichev.com/progcalc
## References
[1] https://en.wikipedia.org/wiki/Order_of_operations
[2] https://beginners.re/
[3] https://yurichev.com/writings/Math-for-programmers.pdf
[4] https://web.archive.org/web/20190915025154/http://www.hackersdelight.org/
[5] http://reference.wolfram.com/language/tutorial/UsingPreviousResults.html