# 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