So, once upon a time I needed to compare really big numbers in batch (file sizes). I found a nice little COMPARE utility by Brian Williams and liked how clever it was. He also wrote ADD (which I also needed) and MULTIPLY (which I did not need) utilities for really big numbers, which I also liked for their cleverness... so I re-wrote them to try and make them clever-er. Then I thought... that's two out of four. If I added subtraction and division... there's really nothing that couldn't be done (mathematically speaking), right?
math.cmd is an 'expression parser' because I saw the term while researching the shunting-yard algorithm and thought it sounded brainy, I'm not even sure it's true. It uses brute-force style string manipulation to add, subtract, multiply, and divide insanely large decimal numbers (probably thousands of digits), and the parser accepts parenthesis, functions, assignments, etc. I was nearly done with optimizations, but now the only ones left require macros so here's my before shot... let's see how much faster some macros can make it. Brief word of warning, I'm not a math guy. I had to (at least minimally) research the procedure for literally everything here except addition, and the shunting-yard algorithm is ~90% straight pseudo-code. Run with no options or double-click to bring up the help screens, there's a little interactive bit at the end.
Here's the help screens, zipped file is below.
Code: Select all
:math [/An|/Dn|/Mn|/Rn|/Sn|/Un|/H|/?] In-line_Math_Expression [;...] v0.2
::
:: Full-featured expression parser written in 100% native WinNT batch script.
:: Supports functions, assignments, numbers to thousands of decimal places,
:: and provides the following full range of operators in order of precedence:
::
:: Highest : $ Exponent : & NthRoot
:: : * Multiply : / Divide : @ Modulus
:: : + Addition : - Subtraction
:: : <=> Raw Compare (returns 1=greater, -1=less, 0=equals)
:: : < LessThan : > GreaterThan : <= LessOrEqual : >= GreaterOrEqual
:: : ## EqualTo : <> NotEqualTo (comparisons return 1=true, 0=false)
:: Lowest : = Equals (assignment) : ; Expression Separator
::
:: Operations are left-to-right, except for '$ &' which are right associative.
:: '<>^&' must be wrapped in double-quotes. If needed, # can always replace =.
::
:: Operands may be digits and/or variables and expressions can be of any size.
:: Result of the operation is always returned in the user variable {math}.
:: Up to 32 return variables may be assigned (eg. var1=var2=x*y+(var3=z-1)).
::
:: ECHO[n] may be used as a returnVariable to echo result with [n] line feeds.
:: {math*} and {_math*} are reserved variable names and should not be used.
::
:: ------ Many standard functions are available for use in expressions ------
::
:: abs(num) returns the absolute value of num
:: avg(num1,num2[,numX]) returns the average value of a given list of values
:: ceil(num) returns the nearest integer not less than num
:: dim(num1,num2) returns the positive difference between num1 and num2
:: e(#decimals) returns the constant 'e' to given number of decimals
:: fact(num) returns the factorial of int(num)
:: floor(num) returns the nearest integer not greater than num
:: gcd(num1,num2) returns the greatest common divisor of two values
:: if(cond,true[,false]) returns {true} if cond <> 0, {false} or 0 if cond = 0
:: int(num) returns the nearest integer with magnitude <= num
:: lcm(num1,num2) returns the least common multiple of two values
:: max(num1,num2[,numX]) returns the largest of the given list of values
:: min(num1,num2[,numX]) returns the smallest of the given list of values
:: nth(base,exp) returns the exp-th root of the given base value
:: pi(#decimals) returns the constant 'pi' to given number of decimals
:: pow(base,exp) returns the given base raised to the power of exp
:: sign(num) returns 1 if num > 0, 0 if num = 0, or -1 if num < 0
::
:: The left parenthesis is part of the function name, no white-space is allowed.
:: Functions may have an unlimited number of parameters and are easily mod-able,
:: simply follow the pattern in the script and add the name to the function list.
::
:: ----- If operation is successful the following ErrorLevel is returned -----
:: -1 Negative\LessThan : 0 Zero\EqualTo\False : 1 Positive\GreaterThan\True
::
:: ---- If operation is unsuccessful the following ErrorLevel is returned ----
:: 2 Non-numeric operand : 3 Missing operand : 4 Missing operator
:: 5 Unsupported operator : 6 Unbalanced parenthesis : 7 Divide by zero error
:: 8 Non-integer radical : 9 Negative root undefined : 10 Function parameter
:: 11 Assignment error : 12 Negative factorial
::
:: --- Environment Variables shown with /Command Switch and Default Values ---
:: _MATH_ABSOLUTE_VALUE /A0 0=respect negative input, 1=all input is positive
:: _MATH_MAX_DECIMAL /D9 9=maximum number of decimal places to return
:: _MATH_MODULUS_UNARY /M0 0=dividend, 1=divisor, 2=always positive
:: _MATH_ROUND_UP_ON /R5 5=round up on this value or greater, 0=truncate
:: _MATH_SHOW_RESULT /S0 0=never, 1=if no assignment, 2=expression, 3=both
:: _MATH_SHOW_UNARY /U0 0=negative only, 1=both, 2=none (absolute value)
::
:: /?, /H, or a lack of math expression will display this help screen.
::
:: If numbers are small enough SET /A is used for fast decimal arithmetic. The
:: script keeps track of decimal and unary adding them back to the result, but
:: when adding 9+, dividing by 9+, subtracting 10+, or multiplying by 10+ digits
:: batch routines and recursive calls are used, which are exponentially slower.
::
:: Examples:
:: math.cmd /d15 echo = 245850922 / 78256779
:: Returns '3.141592653589793' in variable {math} + echo to the screen (no LF).
::
:: math.cmd result=a1+100/(b1-4)*3
:: Parses {a1} + ((100 / ({b1} - 4)) * 3) and places answer in {result}.
:: Variable assignments must always begin a block (eg. after left parenthesis).
::
:: math.cmd /d256 /r0 /a1 echo1=answer=myVarName
:: Returns the absolute value in {myVarName} truncated to 256 decimals using
:: variable {answer} and echos the value (1 LF). 32 return variables are allowed.
::
:: math.cmd "a1=avg(n1,n2,n3,n4); a2=avg(n5,n6,n7,n8); highAvg=if(a1 > a2,a1,a2)"
:: Returns the average of the two lists in {a1} and {a2} and returns the greater
:: of the two averages in {highAvg}. Note the quotes are required for '<>^&'.
::
:: math.cmd "value1 >= value2" && GOTO :false || GOTO :true
:: Returns ERRORLEVEL=1 if true, 0 if false. Provides a full range of comparison
:: operators and can evaluate numbers of virtually any size (large or small).
::
:: math.cmd guess = (((root - 1) * guess) + (base / (guess $ (root - 1)))) / root
:: Computes the {root} of {base} number through convergence starting at {guess}.
:: Run in a loop, gets more accurate each time. Very slow, start with best guess.
::
:: math.cmd v0.2 2017/09/21 written by CirothUngol.
:: Inspired by Brian Williams' Big Math batch utilities.
:: http://www.robvanderwoude.com/battech_math.php
::
:: v0.2 2017/09/21
:: Replaced functions for removing leading and trailing zeros with macros.
:: Replaced all error GOTOs and exit GOTOs with macros.
:: Added ';' as an expression separator, now supports multiple expressions.
:: Changed SHOW_RESULT to display only if no assignment, emulates SET /A.
:: Increased number of allowed return variables from 9 to 32.
:: Fixed macros to load correctly before or after the SETLOCAL.
::
:: v0.1 2017/09/15
:: Initial release on DOStips.