printf.exe: Arithmetic and Programming
Posted: 25 Jul 2016 19:26
I recently remembered a new .exe auxiliary program I completed a couple months ago, so I decided to post it now. It is called printf.exe and is a double-purpose program: emulate the printf C function in order to show formatted output of strings and numbers, and evaluate arithmetic expressions of 32/64-bits integers and floating point numbers. This program don't really emulate printf C function, but it is just a wrapper for it: the program take the parameters and accomodate they in the stack that is used to invoke any Windows API function; then, just invoke the Windows C run-time printf function.
To evaluate arithmetic operations I used the same stack and just get the numbers from it, perform the operation in RPN (postfix) notation and store the result back in the stack! For example: printf "format" 1 2 3 have 3 numeric parameters: 1, 2 and 3; and printf "format" 1 2 3 + 4 have also 3 numeric parameters: 1, 5 (the sum of 2+3) and 4. This scheme makes possible that the program be not too complex; as a matter of fact, the most complicated part was the reading of numbers and its conversion into the appropriate internal format (I also just copied the floating point routines from another larger project I am currently developing).
In order to successfully use this program you must comprehend that printf.exe is a strongly typed application. The C printf function is very strict in the types of the arguments indicated in the format specification string vs. the real type of the parameters: if these types not match, or there are more format specifications than parameters, the result is "unpredictable" (as the documentation said), so non sense results may be displayed or even a Windows run-time error may occur.
This restriction is automatically transferred into printf.exe program, because it just invoke the exact same function. However, this strict rule is also applied in the arithmetic operations! You must give first one or two numbers with a given type (integer or float) and then an operator that match the type of previous numbers; this point not only means that there are two separate sets of operators for integer and floating-point numbers, but that even in certain integer operations the numbers must be both of 64-bits, both of 32-bits, or the first one 64-bits and the second one 32-bits! This is done to perform the operations in the exact same way they are achieved in the CPU, so no previous nor posterior conversions be necessary. Of course, all these rules imply that printf.exe program is somewhat difficult to use at first, but if the program would be designed to be more tolerant and perform type conversions automatically (like many compilers and interpreters) the resulting program would be much larger and complex, and I never would started to write it.
You may download printf.exe auxiliary program and the two next companion files from this printf.zip attached file: This is printf.txt:
This is printf-Examples.bat:
Enjoy it!!!
---------------------------------------------------------------------
A completely new version of printf.exe is here! The new printf.exe version 2.11 have A LOT of new and useful features. The 64 bits integer numbers have been eliminated so the creation of arithmetic expressions is much simpler now, with just 32-bits integers (and the same floating point numbers). The new printf 2.11 have now character strings management, so it can be used to generate a new set of text-based results. The most important point though is that printf 2.11 now has scripting capabilities based on the simplest programming scheme that still provides the same functionality as modern structured programming languages.
You can review and download the new printf.exe version 2.11 from this link.
---------------------------------------------------------------------
I completed and posted a new printf.exe version 2.55 with a couple new features, like show text in color. You can download it from this link.
Antonio
To evaluate arithmetic operations I used the same stack and just get the numbers from it, perform the operation in RPN (postfix) notation and store the result back in the stack! For example: printf "format" 1 2 3 have 3 numeric parameters: 1, 2 and 3; and printf "format" 1 2 3 + 4 have also 3 numeric parameters: 1, 5 (the sum of 2+3) and 4. This scheme makes possible that the program be not too complex; as a matter of fact, the most complicated part was the reading of numbers and its conversion into the appropriate internal format (I also just copied the floating point routines from another larger project I am currently developing).
In order to successfully use this program you must comprehend that printf.exe is a strongly typed application. The C printf function is very strict in the types of the arguments indicated in the format specification string vs. the real type of the parameters: if these types not match, or there are more format specifications than parameters, the result is "unpredictable" (as the documentation said), so non sense results may be displayed or even a Windows run-time error may occur.
This restriction is automatically transferred into printf.exe program, because it just invoke the exact same function. However, this strict rule is also applied in the arithmetic operations! You must give first one or two numbers with a given type (integer or float) and then an operator that match the type of previous numbers; this point not only means that there are two separate sets of operators for integer and floating-point numbers, but that even in certain integer operations the numbers must be both of 64-bits, both of 32-bits, or the first one 64-bits and the second one 32-bits! This is done to perform the operations in the exact same way they are achieved in the CPU, so no previous nor posterior conversions be necessary. Of course, all these rules imply that printf.exe program is somewhat difficult to use at first, but if the program would be designed to be more tolerant and perform type conversions automatically (like many compilers and interpreters) the resulting program would be much larger and complex, and I never would started to write it.
You may download printf.exe auxiliary program and the two next companion files from this printf.zip attached file: This is printf.txt:
Code: Select all
Emulates printf C run-time function; evaluates RPN arithmetic expressions.
printf "format specification" {number [operator]|'c'|"string"|variable} ...
This program is a wrapper for the C run-time printf function described at
https://msdn.microsoft.com/en-us/library/wc7014hz.aspx
so all specifications and requirements described at that site apply.
Additionally, this program evaluates arithmetic expressions in RPN.
The first parameter is a standard printf format specification given as
"string literal" or in a Batch variable. This is a quick reference guide of it:
%[flags][width][.precision][size]type
-+ 0# ----- ---------- ---- cCdiouxXeEfgGaAps
Insert "ll" - | left align c | character
in [size] + | insert + sign if positive C | wide (16 bits) Character
for 64-bits | insert space for + sign d | integer (Decimal)
"long-long" 0 | zero pad i | Integer = d
integers. # | insert . in g type, or o | integer (Octal)
| insert 0|0x in o|x types u | integer (Unsigned)
x | integer (hexadecimal)
[width] Minimum/total width (no truncate): X | integer (HEXADECIMAL)
printf "%05i" 3 = "00003" e | double ([-]d.ddddeñddd)
E | double (like e, with E)
[.precision] Maximum width (truncate/round): f | double ([-]ddd.dddddd)
printf "%.4f" 3.141592654 = "3.1416" g | double (shorter of f|e)
G | double (like g, with E)
An asterisk in place of width and/or precision a | double ([-]0xh.hhhpñddd)
get its value from the next (int) parameter: A | double (like a, with P)
printf "%0*i" 5 3 = "00003" p | string (address in hex)
printf "%0*.*f" 8 4 3.141592654 = "003.1416" s | string
String literals can not include embedded quotes, but Batch variables can.
Standard control characters \n \r \t \a \b are valid in string literals only;
to insert control characters in a format specification stored in a variable,
use %c format with the Ascii code of the character given as int. For example:
printf "Line one\n\t\tLine two\n"
set "format=Line one%c%c%cLine two%c"
printf format 10 9 9 10
The parameters after the first one can be of one of these types:
- A number, if it start in digit or minus sign.
- A character, if it is enclosed in apostrophes (managed as a 32-bits integer).
- A string literal, if it is enclosed in quotes.
- A string Batch variable otherwise.
Numbers are converted into one of the following sub-types:
- A double-precision 64-bits floating-point number if it includes a decimal
point or an exponent of ten greater than 0 (standard floating-point format).
- A "long-long" 64-bits integer number if its value exceed the maximum of a
32-bits integer one, or if the number ends in "ll" size specification.
- A 32-bits integer number otherwise.
An integer number may be given in the standard octal/hexadecimal notation;
the largest integer may have: {18 decimal|16 hexadecimal|21 octal} digits.
--> If the format specifications does not match the type of the parameters,
or there are more specifications than parameters, this program may fail.
Arithmetic operations in Reverse Polish Notation (RPN) may be evaluated via
two *separate sets* of operations for integer and floating-point numbers.
Reference: https://en.wikipedia.org/wiki/Reverse_Polish_notation
You may learn RPN at: http://www.hpmuseum.org/rpn.htm
or in this extensive tutorial: http://hansklav.home.xs4all.nl/rpn/index.html
Most integer operations works on 64-bits numbers. One-operand operators:
! BoolNot, ~ BitNot, _ ChngSign, $ Signum, ] Store, [ Recall, @ Dup, ? Random.
Two-operands operators: + Add, - Subtract, * Multiply, / Quotient, % Remainder,
< Min, > Max, << BitSHL, >> BitSHR, & BitAnd, | BitOr, ^ BitXor, # Exchange.
The * operator multiply two 32-bits numbers and produce a 64-bits result.
In / % << >> operators the second operand must be a 32-bits number. Quotient
and Remainder produce a 32-bits result; if the divisor is zero, these operators
return the high or low 32-bits part of the number, respectively. If "u" letter
is added after * / % << >> operators, an unsigned operation is performed.
The ? operator enter a random number between 0 and 32767 as a 32-Bits number.
To convert a 32-bits number to a 64-bits one, multiply it by 1:
printf "Random as 32-bits: %i. Random as 64-bits: %lli.\n" ? ? 1 *
To convert a 64-bits number to a 32-bits one, get its remainder by 0:
printf "Random as 64-bits: %lli, and as 32-bits: %i.\n" ? 1 * @ 0 %
Floating-point functions operate float numbers *only*. One-operand functions:
NOT, CHS, ABS, SIGN, INT, FRAC, INV, X^2, SQRT; SIN, COS, TAN, ASIN, ACOS, ATAN
(in radians), to DEGrees, to RADians; LN, LOG, EXP, EXPT; DUP, STO. RCL, PI.
Two-operands functions: ADD, SUB, MUL, DIV, MOD, POW, MAX, MIN, XCHG. CLST.
Float operations use the FPU stack, so a run-time error is never issued: the
"special values" defined in IEEE 754 floating-point specification are returned.
Reference: https://en.wikipedia.org/wiki/Floating_point#Special_values
FRAC, TAN, ASIN, ACOS, ATAN, LN, LOG, EXP, EXPT require one empty FPU register.
--> If numbers and operator types don't match (32-bits int/64-bits int/float),
or there are not enough numbers for an operation, this program will fail.
A couple simple examples:
printf "3 plus 5 = %lli, 3 minus 5 = %lli\n" 3ll 5ll + 3ll 5ll -
printf "The result of (4+5)/(6+7) is: %f\n" 4. 5. Add 6. 7. Add Div
The $ Signum operator used after a subtract allows to perform comparisons:
%A%ll %B%ll - $ = -1 if A < B, 0 if A == B, 1 if A > B. After that:
1ll + ! = 1 if A < B, 0 if A >= B; OR
1ll - ! = 1 if A > B, 0 if A <= B.
%A%ll %B%ll - ! = 1 if A == B, 0 if A != B.
If the [.precision] part in a string format is zero, the string parameter is
not shown. This part can be calculated and passed to a format specification via
an asterisk, so another string can be conditionally shown, like a message. Note
that Remainder by zero is used to convert a 64-bits number to a 32-bits one:
set /A A=%random%, B=%random%
printf "%i is%.*s less %i" %A% %A%ll %B%ll - $ 1ll - ! 0 % 4 * 0 % " not" %B%
More examples on most operators/functions are shown in: printf-Examples.bat
==> Remember: One-character operators works on integer numbers; three letters
functions works on floating-point numbers. Do NOT mix they!
At end, the total number of characters shown is returned in ERRORLEVEL.
If an error is found, a negative value is returned this way:
-1 = printf function internal error
-2 = Closing quote/apostrophe missing or bad placed
-3 = Number wrong or too large
-4 = Invalid operator
-5 = Invalid function or undefined variable
Code: Select all
@echo off
setlocal
rem Show several examples on printf.exe auxiliary program usage
rem Antonio Perez Ayala
rem Do NOT use 'more' on the output of this Batch file
mode CON: COLS=80 LINES=200
set "prompt=%~D0\> "
cls
echo on
rem Example session on printf.exe program features.
printf "A char: %%c, a string: %%s, an int: %%i, a float: %%f\n" ^
'X' "Hello" 123 456.789
rem The format specification may be stored in a variable, with quotes,
rem but in this case the standard control characters can not be used:
set "format=A char: '%%c', a string: "%%s", an int: %%i, a float: %%f%%c"
printf format 'X' "Hello" 123 456.789 10
rem Characters are managed as 32-bits integers:
printf "Ascii code of '%%c' is %%i, char. of code %%i is '%%c'" 'A' 'A' 97 97
rem Management of 64-bits "long-long" integers:
printf "Wrong: int1: %%i, long-long1: %%i, long-long2: %%i, int2: %%i\n" ^
123 9876543210 456ll 789
printf "Right: int1: %%i, long-long1: %%lli, long-long2: %%lli, int2: %%i\n" ^
123 9876543210 456ll 789
rem Large integers and octal/hexadecimal notations:
printf "Dec: %%lld, Hex: %%lld, Oct: %%lld\n" ^
123456789012345678 0x123456789ABCDEF0 0123456712345671234567
rem Octal/hexadecimal output formats:
printf "Dec: %%lld, Hex: %%#llX, Oct: %%#llo\n" ^
123456789012345678 0x123456789ABCDEF0 0123456712345671234567
rem Largest signed and unsigned 64-bits integer numbers:
printf "Largest positive: %%lld, largest unsigned: %%llu\n" ^
0x7FFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF
rem Some examples of Reverse Polish Notation expressions:
printf "Number: %%lli, result: %%lli, number: %%lli" 12ll 34ll 56ll + 78ll
printf "Change sign of %%lli is equal to BitNOT the number plus 1: %%lli\n" ^
12345ll @ ~ 1ll +
printf "Max positive product: %%lli (%%#llX)\n" 0x7FFFFFFF 0x7FFFFFFF * @
printf "Max unsigned product: %%llu (%%#llX)\n" 0xFFFFFFFF 0xFFFFFFFF *u @
printf "%%lli divided by %%i is %%i plus %%i in remainder\n" ^
123456789012345 ] 100000 [ 100000 / [ 100000 %%
for /F "tokens=3" %%a in ('dir') do @set "free=%%a"
set "free=%free:,=%"
echo The free space on disk is:
printf "%%lld B = %%lld KB (+%%lldB) = %%lld MB (+%%lldKB) = %%lld GB (+%%lldMB)\n" ^
%free%ll ] [ 0x3FFll ^& [ 10 ^>^> ] # [ 0x3FFll ^& [ 10 ^>^> ] # [ 10 ^>^> [ 0x3FFll ^&
rem Trick to avoid to escape all those special characters:
set "format=The free space on disk is:%%c%%lld B = %%lld KB (+%%lldB) = %%lld MB (+%%lldKB) = %%lld GB (+%%lldMB)%%c"
for /F "delims=" %%a in ("printf format 10 %free%ll ] [ 0x3FFll & [ 10 >> ] # [ 0x3FFll & [ 10 >> ] # [ 10 >> [ 0x3FFll & 10") do %%a
rem Example taken from:
rem http://stackoverflow.com/questions/35767361/
rem a-number-is-both-greater-and-less-than-another-in-a-windows-batch-file
set bakfilesize=399502220288
set freespace=463777075200
printf "baksize is %%.*s%%.*s\n" %bakfilesize% %freespace% - $ 1ll - ! ] ^
0 %% 6 * 0 %% "larger" [ ! 0 %% 16 * 0 %% "smaller or equal"
rem =======================
rem Floating point examples
printf "The result of (4+5)/(6+7) is: %%f\n" 4. 5. Add 6. 7. Add Div
printf "Square root(2)=%%f, Cubic root(64)=%%g, e=%%f, Sin(30)=%%g\n" ^
2. Sqrt 64. 3. Inv Pow 1. Exp 30. Rad Sin
set /A X=4,Y=3
printf "Rectangular (X=%%i, Y=%%i) is Polar (Angle=%%g, Magnitude=%%g)\n" ^
%X% %Y% %Y%. %X%. Div ATan Deg %X%. X^^2 %Y%. X^^2 Add Sqrt
rem Some special values of the IEEE 754 floating-point specification:
printf "Minus zero: %%g, Infinite: %%g, NotANumber: %%g\n" ^
-1. 0. Mul 1. 0. Div -2. Sqrt
rem Up to 8 floating-point numbers can be entered in the FPU stack:
printf "Enter 8 numbers: %%g %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7. 8.
rem If more numbers are entered, a FPU stack *error condition* happen:
printf "Enter 9 numbers: %%g %%g %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7. 8. 9.
rem ... or if the 8th number have a fractional part or exponent NEQ 0:
rem (because in this case an additional Power function must be performed)
printf "8th number use Pow function: %%g %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7. 8.5
rem The next functions cause the same error when the stack is full:
rem (because they use one additional FPU register to achieve its result)
rem Frac, Tan, ASin, ACos, ATan, Ln, Log, Exp, ExpT
printf "Frac on 7th number OK: %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7.5 Frac
printf "Frac on 8th number fail: %%g %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7. 8.5 Frac
rem In order to *display* more numbers, use CLST (CLearSTack) function:
printf "Display 10 numbers: %%g %%g %%g %%g %%g %%g %%g %%g %%g %%g\n" ^
1. 2. 3. 4. 5. 6. 7. CLST 8.5 9. 10.
---------------------------------------------------------------------
A completely new version of printf.exe is here! The new printf.exe version 2.11 have A LOT of new and useful features. The 64 bits integer numbers have been eliminated so the creation of arithmetic expressions is much simpler now, with just 32-bits integers (and the same floating point numbers). The new printf 2.11 have now character strings management, so it can be used to generate a new set of text-based results. The most important point though is that printf 2.11 now has scripting capabilities based on the simplest programming scheme that still provides the same functionality as modern structured programming languages.
You can review and download the new printf.exe version 2.11 from this link.
---------------------------------------------------------------------
I completed and posted a new printf.exe version 2.55 with a couple new features, like show text in color. You can download it from this link.
Antonio