%MM% v0.2a - a full-featured WinNT math macro

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% - a full-featured WinNT math macro

#16 Post by CirothUngol » 10 Aug 2018 16:34

dbenham wrote:
10 Aug 2018 13:12
:shock: That doesn't look right :?
It looks like you initialize the return value to 0 with SET dc=0, and then conditionally set the return value to 1, but using n0 instead of dc. What am I missing :?:
It would probably help if I bothered to delineate exactly what each variable is, I should actually do this for all variables before releasing v0.2, so that improvements could be easier to spot by others:
n0 = final answer
n1,n2,n3 = values from operands, padded with zeros both left and right of the decimal. For add/subtract/compare.
o1,o2,o3 = values from operands, not padded. For multiply/divide.
dc = decimal place for n0 (counted from right to left). This is initially set for add/subtract (because it saved a few characters, probably not anymore) and needs to be cleared because the answer will have 0 decimal places to the right.

Code: Select all

)ELSE IF %%b=="||" (IF !o1! NEQ 0 SET o1=1^&IF !o2! NEQ 0 SET o2=1^&SET /A n0=o1^^o2)%\n%%= 'logical exclusive or /xor =%
It's a bit smaller than my version... but won't it fail if o1=0 because it won't execute the remainder of the line?

dbenham
Expert
Posts: 2219
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: %MM% - a full-featured WinNT math macro

#17 Post by dbenham » 10 Aug 2018 18:46

So n0 must be initialized to 0 before entering the IF, yes?
CirothUngol wrote: It's a bit smaller than my version... but won't it fail if o1=0 because it won't execute the remainder of the line?
Doh - Of course. And I probably need to clear dc as well.

My formula needs parens around each of the IF statements

Code: Select all

)ELSE IF %%b=="||" ((IF !o1! NEQ 0 SET o1=1)^&(IF !o2! NEQ 0 SET o2=1)^&SET /A dc=0,n0=o1^^o2)%= 'logical exclusive or /xor =%%\n%
Or better yet, use multiple lines. It looks longer, but actually is smaller

Code: Select all

%=  =%)ELSE IF %%b=="||" (%= 'logical exclusive or /xor =%%\n%
%=    =%IF !o1! NEQ 0 SET o1=1%\n%
%=    =%IF !o2! NEQ 0 SET o2=1%\n%
%=    =%SET /A dc=0,n0=o1^^o2%\n%
%=  =%)%\n%
Not sure it is smaller anymore. :?

Note - I like putting the %\n% after the comment - it makes it easier to scan and verify each line is terminated properly.


Dave Benham

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: %MM% - a full-featured WinNT math macro

#18 Post by thefeduke » 15 Aug 2018 14:00

The two main posters have me awed. My compliments!

If I understand correctly, the name of the script that creates the macro variable %MM% is of little importance. I observed that it uses no input arguments and that to set up the macro, a minimum of two console interactions are needed after the macro is defined. It struck me that I would like to set up and use this macro in a script while avoiding the need for those two interactions.

To do that, I added this line just before the label :mm_help after the macro was just created:

Code: Select all

If NOT "%~1."=="." GoTo :Eof 
So the demo is bypassed if any argument is passed. Would that indeed cause no problems? It appeared to work for me.

John A.

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% - a full-featured WinNT math macro

#19 Post by CirothUngol » 15 Aug 2018 17:57

Thanks for the compliments! ^_^

Yup, that should work just fine. Only the 'SET \n=' and the 'SET MM=' definitions are required, the macro is otherwise completely self-contained. You could also just copy/paste it into your batchfile removing the need for multiple batch scripts.

The interactive bit is just to display the help, show it's abilities, and allow the user to experiment the some expressions (for me, that's used for testing/troubleshooting).

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% v0.2 - a full-featured WinNT math macro

#20 Post by CirothUngol » 17 Aug 2018 18:04

I've updated the OP with MM v0.2, I was able to add logical, unary, and bitwise operations. The bitwise ops are all passed directly to SET/A, which treats values above 2147483647 as 2147483647 and below -2147483648 as -2147483648 (because they're contained in variables?). I've reduced most of the variables in the Math portion to single letters and eliminated the huge IF...ELSE structure to reduce the size and have cleaned-up all of the remarks so that they're more legible and have included a list of variables used in the script comments.

All of the operators are the same as SET/A except for:
'=Logical Not - exclamation point is just too troublesome, and the tiny apostrophe was the best choice available.
|&=Logical Xor - caret is also far too problematic. A single ^ is OK, but double ^^ is probably a troubleshooting nightmare.
I've used this reference to create/correct the operator precedence. Beyond the operators listed in the docs I also use "{ \ , ` }" as internal substitutes for "^ * ? - ~" so I've quite literally used all of the special characters except for "[ _ ]" which are the only ones allowed in user variables.

My testing was mediocre at best, so again please post if you try it and find any issues.

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% v0.2 - a full-featured WinNT math macro

#21 Post by CirothUngol » 18 Aug 2018 13:48

I was scanning the final v0.2 macro and cleaning up the various versions I went through over the last couple of months (about 12) and think that, other than any corrections for discovered errors, %MM% is done. It might be possible to squeeze out a few dozen more characters, but what else is there?
The idea of inserting strings into error messages has merit. There are 9 ECHOs where errors occur, any of the variables listed in the comments could be included with the error to help illustrate what went wrong. This is originally how I planned the errors messages, but because of size constraint and my desire to have it appear just like SET/A I removed them. Other than that, I think bugfix will be the only other release.

Oh, thanks Dave for archiving v0.1 on the second post of this thread. In my zeal to post v0.2 I simply clobbered on top of it.^_^
Just for grins, I thought I'd post an early version of MM, from when I though I was just writing a quick integer-only macro to add and divide large cumulative file sizes. This is the 4th version I have saved named iMath2.cmd. I didn't change a thing, so the code may be a bit messy:

Code: Select all

:: %iMath% [var=] num|var +|-|x|/|@|GT|LS|EQ|GE|LE|NE num|var [;...]
:: Macro for mathmatical and comparison operations of large integers.
:: Operands containing only -0123456789. are input as integers, else variables.
:: Decimals are truncated and undefined variables are treated as zero.
:: The value of each operation is always returned in variable %iMath_%.
:: Comparison operations will also return ERRORLEVEL 0=false, 1=true.
:: Multiple expressions may be separated by comma[,] or semicolon[;] and
:: spaces must separate all operands from operators other than [=,;].
:: Variable names beginning with 'iMath*' are reserved and should not be used.
:: SET mx=maximum # of digits up to ~256, larger numbers=slower runtimes.
:: Divisor for / and @ is limited to 8 digits, the dividend has no such limit.
::
::      add +: %iMath% 123456789 + 987654321;sets iMath_=1111111110, always
:: subtract -: %iMath% var1=9876543210 - -1234567890;sets var1=1111111110
:: multiply x: %iMath% var2=777.777 x var1;truncate decimal, var2=863333332470
::   divide /: %iMath% var3=var2 / 777.0123456789;divides var2 by 777
::  modulus @: %iMath% var4=var1 @ var3;sets var4=0 as var1=var3=1111111110
:: comparison: %iMath% var4=var1 EQU var3;sets both var4 and ERRORLEVEL = 1
::
@SET mx=16

@ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
:: make zeros for padding
SET z0=0000000000000000
FOR %%A IN (1 2 3) DO CALL SET z0=%%z0%%%%z0%%%%z0%%%%z0%%
CALL SET z0=%%z0:~-%mx%%%

:: setup macro constants
SET ^"LF=^

^"
SET ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
IF "!!"=="" ( SET "b=^^^^^^^!" & SET "q=^^^!"
) ELSE SET "b=!" & SET "q=!"

SET iMath=FOR %%# IN (1 2) DO IF %%#==2 ( SETLOCAL EnableExtensions EnableDelayedExpansion%\n%
	FOR %%A IN (C E0 E1 N0 N1 N2 OP R U0 U1 U2 V) DO SET iMath%%A=%\n%
	SET "iMathH=[var=] num|var +|-|x|/|@|GT|LS|EQ|GE|LE|NE num|var [;...]"%\n%
	IF NOT DEFINED iMathP SET iMathP=A%\n%
	SET iMathP=%b%iMathP:^"=%b%%\n%
	SET iMathP=%b%iMathP:\=/%b%%\n%
	SET iMathP=%b%iMathP:,=;%b%%\n%
	SET iMathP=%b%iMathP:;=^" ^"%b%%\n%
	FOR /F "tokens=1* delims=*" %%A IN ("%q%iMathP%q%") DO IF "%%B" NEQ "" SET iMathP=^& SET iMathE1= 00^& ECHO unknown operator *: %b%iMathH%b%^>^&2%\n%
	FOR %%: IN ("%q%iMathP%q%") DO IF %%: NEQ "" ( SETLOCAL%\n%
		SET iMathT=%%~:%\n%
		FOR /F "tokens=1* delims==" %%A IN (%%:) DO IF "%%B" NEQ "" FOR %%C IN (%%A) DO SET iMathV=%%C^& SET iMathT=%%B%\n%
		FOR /F "tokens=1-3" %%A IN ("%q%iMathT%q%") DO SET iMathOP=%%B^& SET iMathT=%%A %%C%\n%
		FOR %%A IN (%b%iMathT%b%) DO FOR /F "tokens=* delims=-0123456789." %%B IN ("%%A") DO ( SET /A iMathC+=1%\n%
			IF "%%B"=="" ( SET iMathT=%%A%\n%
			) ELSE SET iMathT=%b%%%A%b%%\n%
			IF NOT DEFINED iMathT SET iMathT=0%\n%
			IF "%q%iMathT:~0,1%q%"=="-" SET iMathT=%b%iMathT:~1%b%^& SET iMathU%b%iMathC%b%=-%\n%
			FOR /F "tokens=1 delims=." %%C IN ("%q%iMathT%q%.") DO SET iMathN%b%iMathC%b%=%z0%%%C)%\n%
		SET /A f0=t0=t1=t2=t3=0%\n%
		FOR %%A IN (e0 n0 n1 n2 op u0 u1 u2) DO SET %%A=%b%iMath%%A%b%%\n%
		IF "%q%op%q%"=="+" IF "%q%u2%q%" NEQ "%q%u1%q%" SET u2=%b%u1%b%^& SET op=-%\n%
		IF "%q%op%q%"=="-" IF "%q%u2%q%" NEQ "%q%u1%q%" SET u2=%b%u1%b%^& SET op=+%\n%
		IF "%q%op%q%"=="@" SET op=/^& SET f0=1%\n%
		IF %b%iMathC%b% LSS 2 ( SET e0= 00%\n%
			ECHO missing parameters: %b%iMathH%b%^>^&2%\n%
		) ELSE IF "%q%op%q%"=="+" ( SET u0=%b%u1%b%%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%+%b%n2:~-%%A,1%b%+t0,t0=0%\n%
				IF %b%t1%b% GTR 9 SET /A t1-=10,t0=1%\n%
				SET n0=%b%t1%b%%b%n0%b%)%\n%
		) ELSE IF "%q%op%q%"=="-" (%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%%\n%
				IF %b%t1%b% GTR %b%t2%b% SET t3=1%\n%
				IF %b%t1%b% LSS %b%t2%b% SET t3=2)%\n%
			IF %b%t3%b%==2 ( SET t1=%b%n1%b%^& SET n1=%b%n2%b%^& SET n2=%b%t1%b%%\n%
				IF "%q%u1%q%"=="" SET u0=-%\n%
			) ELSE SET u0=%b%u1%b%%\n%
			IF %b%t3%b% NEQ 0 FOR /L %%A IN (1,1,%mx%) DO (%\n%
				SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%+t0,t0=0%\n%
				IF %b%t1%b% LSS %b%t2%b% SET /A t1+=10,t0=1%\n%
				SET /A t1-=t2%\n%
				SET n0=%b%t1%b%%b%n0%b%)%\n%
		) ELSE IF /I "%q%op%q%"=="x" ( IF "%q%u1%q%" NEQ "%q%u2%q%" SET u0=-%\n%
			FOR /L %%A IN (1,1,%mx%) DO SET _%%A=0%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t0=0,c0=%%A%\n%
				FOR /L %%B IN (1,1,%mx%) DO SET /A t1=%b%n1:~-%%A,1%b%*%b%n2:~-%%B,1%b%+t0,t0=t1/10,_%b%c0%b%+=t1 %% 10,c0+=1%\n%
				SET _%b%c0%b%=%b%t0%b%)%\n%
			FOR /L %%A IN (1,1,%b%c0%b%) DO (%\n%
				SET /A _%%A+=t2,t2=_%%A/10%\n%
				SET n0=%b%_%%A:~-1%b%%b%n0%b%)%\n%
			SET n0=%b%t2%b%%b%n0%b%%\n%
		) ELSE IF "%q%op%q%"=="/" ( IF "%q%u1%q%" NEQ "%q%u2%q%" SET u0=-%\n%
			FOR %%A IN (1 2) DO ( FOR /F "tokens=* delims=0" %%B IN ("%q%n%%A%q%") DO SET n%%A=%%B%\n%
				IF NOT DEFINED n%%A SET n%%A=0)%\n%
			IF %b%n2%b%==1 ( SET n0=%b%n1%b%%\n%
			) ELSE IF %b%n2%b%==0 ( SET e0= 00%\n%
				ECHO Divide by zero error.^>^&2%\n%
			) ELSE IF %b%n1%b% NEQ 0 FOR /L %%A IN (0,1,%mx%) DO IF "%q%n1:~%%A,1%q%" NEQ "" ( SET t1=0%\n%
				IF %b%t0%b%==0 SET t0=%\n%
				SET t0=%b%t0%b%%b%n1:~%%A,1%b%%\n%
				IF %b%t0%b% GEQ %b%n2%b% FOR /L %%B IN (1,1,9) DO IF %b%t0%b% GEQ %b%n2%b% SET /A t1+=1,t0-=n2%\n%
				SET n0=%b%n0%b%%b%t1%b%)%\n%
			IF %b%f0%b% NEQ 0 SET u0=%b%u1%b%^& SET n0=%b%t0%b%%\n%
		) ELSE ( SET /A n0=0,gt=1,lt=-1%\n%
			IF "%q%u1%q%"==""  IF "%q%u2%q%"=="-" SET t0=1%\n%
			IF "%q%u1%q%"=="-" IF "%q%u2%q%"==""  SET t0=-1%\n%
			IF "%q%u1%q%"=="-" IF "%q%u2%q%"=="-" SET /A gt=-1,lt=1%\n%
			IF %b%t0%b%==0 FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%%\n%
				IF %b%t1%b% GTR %b%t2%b% SET t0=%b%gt%b%%\n%
				IF %b%t1%b% LSS %b%t2%b% SET t0=%b%lt%b%)%\n%
			IF /I "%q%op:~0,2%q%"=="GT" ( IF %b%t0%b% GTR 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="LS" ( IF %b%t0%b% LSS 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="EQ" ( IF %b%t0%b% EQU 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="GE" ( IF %b%t0%b% GEQ 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="LE" ( IF %b%t0%b% LEQ 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="NE" ( IF %b%t0%b% NEQ 0 SET n0=1%\n%
			) ELSE SET e0= 00^& ECHO unknown operator %b%op:~0,2%b%: %b%iMathH%b%^>^&2%\n%
			IF %b%n0%b%==1 SET e0= 00)%\n%
		FOR /F "tokens=* delims=0" %%A IN ("%q%n0%q%") DO SET n0=%%A%\n%
		IF NOT DEFINED n0 SET n0=0%\n%
		IF %b%n0%b% NEQ 0 SET n0=%b%u0%b%%b%n0%b%%\n%
		FOR /F "tokens=1-3 delims=;" %%A IN (^""%q%iMathV%q%";"%q%n0%q%";"%q%e0%q%"^") DO (%\n%
			ENDLOCAL%\n%
			IF %%A NEQ "" SET %%~A=%%~B^& SET iMathR=%b%iMathR%b%" "%%~A=%%~B%\n%
			SET iMath_=%%~B%\n%
			SET iMathE1=%%~C))%\n%
	FOR /F "tokens=1-3 delims=;" %%A IN (^""%q%iMathR%q%";"%q%iMath_%q%";"COLOR%q%iMathE1%q%"^") DO (%\n%
		ENDLOCAL%\n%
		FOR %%: IN (%%A) DO IF %%: NEQ "" SET %%:%\n%
		SET iMath_=%%~B%\n%
		%%~C)%\n%
) ELSE SET iMathP=

GOTO :m2

:m1
SET xx=Q
SET/P xx=
IF /I "%xx%" NEQ "Q" (
	%iMath% %xx%
	IF ERRORLEVEL 1 ( ECHO Yup ) ELSE ( ECHO Nerp )
	CALL ECHO %%iMath_%% : %%ERRORLEVEL%%
	GOTO :m1
) ELSE SET
IF "!!"=="" ENDLOCAL
EXIT /B 0

:m2
SET rn1=%RANDOM%
SET rn2=%RANDOM%
SET rn3=7%rn1%%RANDOM%
SET rn4=4%rn2%%RANDOM%
ECHO %rn3:~0,9% : %rn4:~0,9%
SET /A x1=%rn3:~0,9% + %rn4:~0,9%
%iMath% x2=%rn3:~0,9% + %rn4:~0,9%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% - %rn4:~0,9%
%iMath% x2=%rn3:~0,9% - %rn4:~0,9%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,6% * %rn4:~0,3%
%iMath% x2=%rn3:~0,6% x %rn4:~0,3%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% / %rn4:~0,8%
%iMath% x2=%rn3:~0,9% / %rn4:~0,8%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% %% %rn4:~0,8%
%iMath% x2=%rn3:~0,9% @ %rn4:~0,8%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
GOTO :m2
EXIT /B 0
It's the last version before I added the parser and it's much faster without the decimal+shunting-yard overhead. Just thought I'd post it as a curiosity before I eventually delete the earlier versions.

dbenham
Expert
Posts: 2219
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: %MM% v0.2a - a full-featured WinNT math macro

#22 Post by dbenham » 18 Aug 2018 16:48

Regarding v0.2 - Great job, especially with the extensive documentation and in-line comments :!: :D

The addition of the major section header comments at the beginning of the lines really helps get your bearings. And the variable documentation is also great.

All in all - very impressive.


Dave Benham

Meerkat
Posts: 80
Joined: 19 Jul 2015 02:27
Location: Philippines

Re: %MM% v0.2a - a full-featured WinNT math macro

#23 Post by Meerkat » 01 Sep 2018 21:52

Hello! Impressive work!

Maybe this was already addressed. When I open the script from CMD, the nth root works. However, when I open the script from clicking, it aborts.
Output wrote:Finding 2th root of 10 to 4 decimals starting at 3
guess=(((2-1)*3)+(10/3$(2-1)))/2
guess#1=3.1666
guess#2=3.1622
guess#3=3.1622
Error : Abort
Meerkat

Post Reply