Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Arbitrary Precision Math

41 views
Skip to first unread message

Frank P. Westlake

unread,
Mar 28, 2012, 11:41:41 AM3/28/12
to
:: apm.cmd
@Goto :main

This is the beginning of an arbitrary precision math subroutine for
CMD only requirements. This version includes only addition,
subtraction, and multiplication operations, and that might be as far as
I will take it. Recursion has been failing so division, binary
operations, and base conversion would add significant bulk to what I
hope to keep small.

If all you need in your script is one of the operations which are
supported (addition or subtraction or multiplication) then you can
safely remove the others.

Frank

:main
@Echo OFF
SetLocal EnableExtensions EnableDelayedExpansion
Set commandLine=%*

If NOT DEFINED commandLine (
Echo(%~f0
Echo(
Echo USAGE
Echo Called as a command from the commandline:
Echo %~f0 nameOfVariable=operand1 operator operand2
Echo(
Echo Called as a command from a script:
Echo CALL %~f0 nameOfVariable=operand1 operator operand2
Echo(
Echo Called as a subroutine from within a script:
Echo Copy the entire subroutine ':apm' into your script then:
Echo CALL :apm nameOfVariable=operand1 operator operand2
Echo(
Echo Operands must be integers.
Echo Operator may be one of:
Echo + addition
Echo - subtraction
Echo * multiplication
Echo(
Echo In all cases the variable named by parameter 1 will contain the
Echo solution.
Echo(
Echo Example:
Echo Call :apm num=1234567890123456789 * 1234567890123456789
Call :apm num=1234567890123456789 * 1234567890123456789
Echo ECHO num=%%num%%
Echo num=!num!
Echo(
) Else (
EndLocal
Call :apm %*
)

Goto :EOF

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Arbitrary Precision Math
Version: 2012-03-28
Author: Frank P. Westlake
License: None required. This script is in the public domain.
Description: Performs addition, subtraction, and multiplication of integers
of arbitrary length.
Operators: + (addition), - (subtraction), * (multiplication).
Usage: CALL :apm nameOfVariable=operand1 operator operand2
Example: CALL :apm num=1234567890123456789 * 1234567890123456789
The variable 'num' equals
1524157875323883675019051998750190521
:apm nameOfVariable operand1 operator operand2
SetLocal EnableExtensions EnableDelayedExpansion
For /F "tokens=1 delims==" %%a in ('Set "#" 2^>NUL:') Do Set "%%a="
REM Get parameters.
Set "#var=%~1"
Set "#A.t=%~2"
Set "#op=%~3"
Set "#B.t=%~4"
REM Record and remove signs.
If "%#A.t:~0,1%" EQU "-" (Set "#A.neg=-" & Set "#A.t=%#A.t:~1%")
If "%#B.t:~0,1%" EQU "-" (Set "#B.neg=-" & Set "#B.t=%#B.t:~1%")
Set /A "#A.len=0, #B.len=0"
:: Separate digits with space.
For %%a in (0 1 2 3 4 5 6 7 8 9) Do (
Set "#A.t=!#A.t:%%a=%%a !"
Set "#B.t=!#B.t:%%a=%%a !"
)
:: Reverse string and count lengths.
For %%a in (%#A.t%) Do (Set "#A=%%a!#A!" & Set /A "#A.len+=1")
For %%a in (%#B.t%) Do (Set "#B=%%a!#B!" & Set /A "#B.len+=1")
If %#A.len% LSS %#B.len% (Set /A "#C.len=#B.len") Else (Set /A
"#C.len=#A.len")
Set /A "#A.i=#A.len-1, #B.i=#B.len-1, #C.i=#C.len-1"
Set "#A.t=" & Set "#B.t="
REM %1 %2 %3 IF MAKE DO
REM +a / +b a>b --- divide, make positive
REM +a / +b a<b --- divide, make positive

REM +a * +b --- multiply, make positive
REM -a * -b a*b multiply, make positive
REM +a * -b a*b multiply, make negative
REM -a * +b a*b multiply, make negative
REM +a + +b --- add, make positive
REM -a + -b a+b add, make negative
REM -a + +b a>b a-b subtract, make negative
REM -a + +b a<b b-a swap, subtract, make negative
REM +a + -b a>b a-b subtract, make positive
REM +a + -b a<b b-a swap, subtract, make negative
REM -a - -b a>b a-b subtract, make negative
REM -a - -b a<b b-a swap, subtract, make positive
REM +a - -b a+b add, make positive
REM -a - +b a+b add, make negative
REM +a - +b a>b a-b subtract, make positive
REM +a - +b a<b b-a swap, subtract, make negative
:: Determine operand order and change operation if necessary.
If "!#op!" EQU "/" Set "#findGreater=1
If "!#op!" EQU "+" (
REM Compare sizes if -+, +- but not ++, --
If "%#A.neg%%#B.neg%" EQU "-" Set "#findGreater=1
)
If "!#op!" EQU "-" (
REM Compare sizes if --, ++ but not: +-, -+
If "%#A.neg%%#B.neg%" EQU "--" (
Set "#findGreater=1"
) Else If "%#A.neg%%#B.neg%" EQU "" (
Set "#findGreater=1"
)
)
:apm.findGreater
If DEFINED #findGreater (
If %#A.len% LSS %#B.len% (
Set "#B.greater=1"
) Else If %#A.len% EQU %#B.len% (
For /L %%i in (%#C.i%, -1, 0) Do (
If NOT DEFINED #STOP (
Set /a "#nA=!#A:~%%i,1!, #nB=!#B:~%%i,1!"
If !#nB! LSS !#nA! (
Set "#STOP=1"
) Else If !#nB! GTR !#nA! (
Set "#B.greater=1"
Set "#STOP=1"
)
)
)
)
)
:: Finalize operator and set sign of result.
If "!#op!" EQU "*" (
If "!#A.neg!" NEQ "!#B.neg!" Set "#C.neg=-"
) Else If "!#op!" EQU "+" (
If "%#A.neg%%#B.neg%" NEQ "-" (
Set "#op=-"
Set "#swap=!#B.greater!"
)
If DEFINED #A.neg (
Set "#C.neg=-"
) Else If DEFINED #B.greater (
Set "#C.neg=-"
)
) Else If "!#op!" EQU "-" (
If "%#A.neg%%#B.neg%" EQU "-" (
Set "#op=+"
) Else (
Set "#swap=!#B.greater!"
)
If DEFINED #A.neg (
If NOT DEFINED #B.greater Set "#C.neg=-"
) Else (
If DEFINED #B.greater Set "#C.neg=-"
)
)
If DEFINED #swap (
REM A is less than B so swap them.
Set "#A=%#B%"
Set "#B=%#A%"
Set "#A.len=%#B.len%"
Set "#B.len=%#A.len%"
Set "#A.i=%#B.i%"
Set "#B.i=%#A.i%"
Set "#A.neg=%#B.neg%"
Set "#B.neg=%#A.neg%"
Set "#A.greater=%#B.greater%"
Set "#B.greater=%#A.greater%"
)
:: At this point #A and #B have .len, .i, and possibly .neg, .greater.
:: #C has .len and .i for the longest of #A and #B, and possibly .neg.
Goto %0.%~3
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.*
Set /A "#carry=0, #C=0"
Set "#z="
For /L %%b in (0, 1, %#B.i%) Do (
Set "#L=!#z!"
For /L %%a in (0, 1, %#A.i%) Do (
Set /A "#n=#carry + !#A:~%%a,1! * !#B:~%%b,1!"
Set /A "#carry=#n/10, #n%%=10"
Set "#L=!#L! !#n!"
)
If !#carry! NEQ 0 (
Set "#L=!#L! !#carry!"
Set /A "#carry=0"
)
Set "#T=!#C!"
Set "#C="
Set /a "#i=0"
For %%n in (!#L!) Do (
For /L %%i in (!#i!,1,!#i!) Do Set "#n=!#T:~%%i,1!"
Set /A "#n+=#carry+%%n, #carry=#n/10, #n%%=10, #i+=1"
Set "#C=!#C!!#n!"
)
If !#carry! NEQ 0 (
Set "#C=!#C!!#carry!"
Set /A "#carry=0"
)
Set "#z=!#z! 0"
)
Set "#T=!#C!"
Set "#C="
For %%a in (0 1 2 3 4 5 6 7 8 9) Do Set "#T=!#T:%%a=%%a !"
For %%a in (!#T!) Do Set "#C=%%a !#C!"
Goto %0.exit
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.+
For /L %%i in (0, 1, %#C.i%) Do (
Set "#nA=!#A:~%%i,1!"
Set "#nB=!#B:~%%i,1!"
Set /a "#n=#nA+#nB+#carry"
Set /A "#carry=0"
If !#n! GEQ 10 (Set /A "#n-=10, #carry=1")
Set "#C=!#n!!#C!"
)
If %#carry% EQU 1 Set "#C=1!#C!"
Goto %0.exit
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.-
:: 10s complement subtraction.
For /L %%i in (0, 1, %#C.i%) Do (
Set "#nA=!#A:~%%i,1!"
Set "#nB=!#B:~%%i,1!"
Set /a "#n=#nA+9-#nB"
Set "#C=!#C! !#n!"
)
Set /A "#carry=1"
For %%a in (%#C%) Do (
Set /A "#n=%%a+#carry, #carry=0"
If !#n! GEQ 10 Set /A "#n-=10, #carry=1"
Set "#D=!#n! !#D!"
)
set "#C=%#D%"
Goto %0.exit
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.exit
For %%a in (!#C!) Do (
Set "#ans=!#ans!%%a"
if !#ans! EQU 0 Set "#ans="
)
If NOT DEFINED #ans (
Set "#ans=0"
) Else (
Set "#ans=!#C.neg!!#ans!"
)
EndLocal & Set "%#var%=%#ans%"
Goto :EOF

jeb

unread,
Mar 29, 2012, 2:27:44 AM3/29/12
to
On 28 Mrz., 17:41, "Frank P. Westlake" <frank.westl...@gmail.net>
wrote:
Hi Frank, you have to much time :-)

but I like this type of code (btw. I had build a similiar batch file
some time ago)
But you could improve the maximum allowed length of the numbers, if
you could also use variable names as operands.
This would increase the max length to ~8100 digits, important when
using additions/subtractions.

jeb

Dr J R Stockton

unread,
Mar 29, 2012, 2:41:50 PM3/29/12
to
In alt.msdos.batch.nt message <jkvbfv$aka$1...@news.albasani.net>, Wed, 28
Mar 2012 08:41:41, Frank P. Westlake <frank.w...@gmail.net> posted:

>
>This is the beginning of an arbitrary precision math subroutine for
>CMD only requirements.

You can check it against my Pascal or Delphi longcalc.exe, respectively
from <http://www.merlyn.demon.co.uk/programs/index.htm>
and <http://www.merlyn.demon.co.uk/programs/32-bit/index.htm>.

Prompt>longcalc cof 7 142857 mul wrt

LONGCALC: www.merlyn.demon.co.uk >= 2005-07-22
compiled with Borland Delphi.
+999999

or in Hex, decimal input

Prompt>longcalc 16 bas cof 7 142857 mul wrt

LONGCALC: www.merlyn.demon.co.uk >= 2005-07-22
compiled with Borland Delphi.
+F423F

or in Hex, Hex input

Prompt>longcalc 16 bas cof 07 0142857 mul wrt

LONGCALC: www.merlyn.demon.co.uk >= 2005-07-22
compiled with Borland Delphi.
+8D1A61

Type longcalc alone for initial Help, and see
Help : ? / ?? / ??? / ?<opr> ; # ends the program.
which are entered at longcalc's internal prompt.

For automatic test, construct a batch file with such a line, and read
standard output.

Prompt>longcalc (Answer ) wrt 16 bas cof 07 0142857 mul wrt
yields Answer +8D1A61


--
(c) John Stockton, nr London UK. ?@merlyn.demon.co.uk BP7, Delphi 3 & 2006.
<http://www.merlyn.demon.co.uk/> TP/BP/Delphi/&c., FAQqy topics & links;
<http://www.bancoems.com/CompLangPascalDelphiMisc-MiniFAQ.htm> clpdmFAQ;
NOT <http://support.codegear.com/newsgroups/>: news:borland.* Guidelines

Frank P. Westlake

unread,
Mar 30, 2012, 8:49:49 AM3/30/12
to
On 2012-03-29 11:41, Dr J R Stockton wrote:
> You can check it against my Pascal or Delphi longcalc.exe...

No. Will it do 12345678901234567890123456789 *
12345678901234567890123456789? I test it with my C arbitrary precision
math program MATH.EXE which is available on the Internet.


Regarding my own message:

On 2012-03-28 08:41, Frank P. Westlake wrote:
> This is the beginning of an arbitrary precision math subroutine for
> CMD only requirements. This version includes only addition,
> subtraction, and multiplication operations, and that might be as far
> as I will take it. Recursion has been failing so division, binary
> operations, and base conversion would add significant bulk to what I
> hope to keep small.

It now is recursive and I'm adding division and bit operations. It will
be a monster.

> If all you need in your script is one of the operations which are
> supported (addition or subtraction or multiplication) then you can
> safely remove the others.

That statement was written before I made some changes that require
addition and subtraction to coexist. An example: "1 + -2" is changed to
"2 - 1" with the result changed to negative.

Frank

Frank P. Westlake

unread,
Mar 30, 2012, 9:02:04 AM3/30/12
to
On 2012-03-30 05:49, Frank P. Westlake wrote:
> An example: "1 + -2" is changed to
> "2 - 1" with the result changed to negative.

Oops.

Frank

Dr J R Stockton

unread,
Mar 31, 2012, 1:44:49 PM3/31/12
to
In alt.msdos.batch.nt message <jl4a5r$1ua$1...@news.albasani.net>, Fri, 30
Mar 2012 05:49:49, Frank P. Westlake <frank.w...@gmail.net> posted:

>On 2012-03-29 11:41, Dr J R Stockton wrote:
>> You can check it against my Pascal or Delphi longcalc.exe...
>
>No. Will it do 12345678901234567890123456789 *
>12345678901234567890123456789? I test it with my C arbitrary precision
>math program MATH.EXE which is available on the Internet.

+152415787532388367504953515625361987875019051998750190521, assuming
decimal - calculated in bases 3, 10, 16, and converted if necessary to
10. Checked by square-rooting.

--
(c) John Stockton, nr London UK. ?@merlyn.demon.co.uk DOS 3.3 6.20 ; WinXP.
Web <http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
PAS EXE TXT ZIP via <http://www.merlyn.demon.co.uk/programs/00index.htm>
My DOS <http://www.merlyn.demon.co.uk/batfiles.htm> - also batprogs.htm.

Frank P. Westlake

unread,
Apr 1, 2012, 8:39:53 AM4/1/12
to
On 2012-03-31 10:44, Dr J R Stockton wrote:
> +152415787532388367504953515625361987875019051998750190521, assuming
> decimal - calculated in bases 3, 10, 16, and converted if necessary to
> 10. Checked by square-rooting.

Why base 3 but not base 2? Your application probably won't run on a
base3 system, if it is used at all by then, but it is sometimes useful
to have a binary pattern in character form.

Frank

Frank P. Westlake

unread,
Apr 1, 2012, 1:51:10 PM4/1/12
to
:: apm.cmd
@Goto :main

This could use some testing if anyone is interested.

This is the beginning of an arbitrary precision math subroutine
for CMD.EXE-only requirements. This version includes addition,
subtraction, multiplication, division, AND, and OR operations
and a hexadecimal to decimal conversion.

I couldn't find a fast division algorithm that used only
integers -- it seems they all use a floating point
library -- so I made something up that is fairly fast except
when the difference in sizes of the dividend and divisor is
great.

AND, OR, division, and multiplication may be removed if not
needed but addition and subtraction must coexist.

Frank

:main
@Echo OFF
SetLocal EnableExtensions EnableDelayedExpansion
Set commandLine=%*

If NOT DEFINED commandLine (
Echo(%~f0
Echo(
Echo USAGE
Echo Called as a command from the commandline:
Echo %~f0 nameOfVariable=operand1 operator operand2
Echo(
Echo Called as a command from a script:
Echo CALL %~f0 nameOfVariable=operand1 operator operand2
Echo(
Echo Called as a subroutine from within a script:
Echo Copy the entire subroutine ':apm' into your script then:
Echo CALL :apm nameOfVariable=operand1 operator operand2
Echo(
Echo Operands must be integers.
Echo Operator may be one of:
Echo + addition
Echo - subtraction
Echo * multiplication
Echo / division
Echo AND AND
Echo OR OR
Echo(
Echo If the operator and second operand are missing then the first
operand
Echo will be saved into the variable; a haxadecimal value is converted to
Echo decimal so this can be used for bighex-to-bigdec conversion.
Echo(
Echo In all cases the variable named by parameter 1 will contain the
Echo solution. If the operation is AND or OR then in addition to setting
Echo the variable specified with the decimal solution, that name with
the
Echo suffix ".hex" will contain the hexadecimal conversion. For example,
Echo if %%1 is "num" then "num" is decimal and "num.hex" is hexadecimal.
Echo(
Echo Example:
Echo Call :apm num=1234567890123456789 * 1234567890123456789
Call :apm num=1234567890123456789 * 1234567890123456789
Echo ECHO num=%%num%%
Echo num=!num!
Echo(
) Else (
EndLocal
Call :apm %*
)

Goto :EOF

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Arbitrary Precision Math
Version: 2012-03-30
Author: Frank P. Westlake
License: None required. This script is in the public domain.
Description: Performs addition, subtraction, multiplication, and
division of
integers of arbitrary length.
Operators: + (addition), - (subtraction), * (multiplication), /
(division).
Usage: CALL :apm nameOfVariable=operand1 operator operand2
Example: CALL :apm num=1234567890123456789 * 1234567890123456789
The variable 'num' equals
1524157875323883675019051998750190521
:apm nameOfVariable operand1 operator operand2
SetLocal EnableExtensions EnableDelayedExpansion
For /F "tokens=1 delims==" %%a in ('Set "#" 2^>NUL:') Do Set "%%a="
REM Get parameters.
Set "#lib=%0"
Set "#var=%~1"
Set "#A.t=%~2"
Set "#op=%~3"
Set "#B.t=%~4"
If /I "!#op!" EQU "AND" Set "#bitOp=1"
If /I "!#op!" EQU "OR" Set "#bitOp=1"
If /I "!#op!" EQU "LS" Set "#bitOp=1"
If /I "!#op!" EQU "RS" Set "#bitOp=1"

If /I "!#A.t:~0,2!" EQU "0x" (
Set "#A.hexIn=1"
Set "#A.t=!#A.t:~2!"
If NOT DEFINED #bitOp (
Set /A "#i=1, #C=0"
Call :apm.hexToDec #A.t=!#A.t!
If NOT DEFINED #op (
Set "#C=!#A.t!"
Goto :apm.exit
)
)
)
If /I "!#B.t:~0,2!" EQU "0x" (
Set "#B.hexIn=1"
Set "#B.t=!#B.t:~2!"
If NOT DEFINED #bitOp (
Set /A "#i=1, #C=0"
Call :apm.hexToDec #B.t=!#B.t!
)
)
:: Record and remove signs.
If "!#A.t:~0,1!" EQU "-" (Set "#A.neg=-" & Set "#A.t=!#A.t:~1!")
If "!#B.t:~0,1!" EQU "-" (Set "#B.neg=-" & Set "#B.t=!#B.t:~1!")
Set /A "#A.len=0, #B.len=0"
:: Separate digits with space.
For %%a in (0 1 2 3 4 5 6 7 8 9 A B C D E F) Do (
Set "#A.t=!#A.t:%%a=%%a !"
Set "#B.t=!#B.t:%%a=%%a !"
)
:: Reverse string and count lengths.
For %%a in (!#A.t!) Do (Set "#A=%%a!#A!" & Set /A "#A.len+=1")
For %%a in (!#B.t!) Do (Set "#B=%%a!#B!" & Set /A "#B.len+=1")
Set "#A.t=!#A.t: =!"
Set "#B.t=!#B.t: =!"
Set /A "#C=0"
If !#A.len! LSS !#B.len! (Set /A "#C.len=#B.len") Else (Set /A
"#C.len=#A.len")
Set /A "#A.i=#A.len-1, #B.i=#B.len-1, #C.i=#C.len-1"
:: Determine operand order and change operation if necessary.
If "!#op!" EQU "/" (
If !#B! EQU 0 (Set /A "1/0" & Goto :EOF)
If "!#A!" EQU "0" (Set "#C=0" & Goto :apm.exit)
Set /A "#A.o=#A.t, #B.o=#B.t, #Q=0"
Set "#Guess="
Call :apm.findGreater
)
If "!#op!" EQU "+" (
REM Compare sizes if -+, +- but not ++, --
If "!#A.neg!!#B.neg!" EQU "-" Call :apm.findGreater
)
If "!#op!" EQU "-" (
REM Compare sizes if --, ++ but not: +-, -+
If "!#A.neg!!#B.neg!" EQU "--" (
Call :apm.findGreater
) Else If "!#A.neg!!#B.neg!" EQU "" (
Call :apm.findGreater
)
)
:: Finalize operator and set sign of result.
If "!#op!" EQU "*" (
If "!#A!" EQU "0" (Set "#C=0" & Goto :apm.exit)
If "!#B!" EQU "0" (Set "#C=0" & Goto :apm.exit)
If "!#A.neg!" NEQ "!#B.neg!" Set "#C.neg=-"
) Else If "!#op!" EQU "/" (
If DEFINED #B.greater (Set "C=0" & Goto :apm.exit)
If "!#A.neg!!#B.neg!" EQU "-" Set "#C.neg=-"
If "!#B!" EQU "1" (Set "#C=!#A.t!" & Goto :apm.exit)
If !#B.len! LEQ 10 (
If !#B.len! LSS 10 (
Set "#op=longDiv"
) Else If "!#B.t!" LEQ "2147483647" (
Set "#op=longDiv"
)
)
) Else If "!#op!" EQU "+" (
If "!#A.neg!!#B.neg!" EQU "-" (
Set "#op=-"
Set "#swap=!#B.greater!"
)
If DEFINED #A.neg (
Set "#C.neg=-"
) Else If DEFINED #B.greater (
Set "#C.neg=-"
)
) Else If "!#op!" EQU "-" (
If "!#A.neg!!#B.neg!" EQU "-" (
Set "#op=+"
) Else (
Set "#swap=!#B.greater!"
)
If DEFINED #A.neg (
If NOT DEFINED #B.greater Set "#C.neg=-"
) Else (
If DEFINED #B.greater Set "#C.neg=-"
)
)
If DEFINED #swap Call :apm.swap
:: At this point #A and #B have .len, .i, and possibly .neg, .greater.
:: #C has .len and .i for the longest of #A and #B, and possibly .neg.
Call :apm.%#op%
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.exit
For %%a in (!#C!) Do (
Set "#ans=!#ans!%%a"
if !#ans! EQU 0 Set "#ans="
)
If NOT DEFINED #ans (
Set "#ans=0"
) Else (
Set "#ans=!#C.neg!!#ans!"
)
EndLocal & Set "%#var%=%#ans%"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.findGreater
Set "#B.greater="
Set "#stop="
If !#A.len! LSS !#B.len! (
Set "#B.greater=1"
) Else If !#A.len! EQU !#B.len! (
For /L %%i in (!#C.i!, -1, 0) Do (
If NOT DEFINED #STOP (
Set /a "#nA=!#A:~%%i,1!, #nB=!#B:~%%i,1!"
If !#nB! GTR !#nA! Set "#B.greater=1"
If !#nB! NEQ !#nA! Set "#STOP=1"
)
)
)
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.length varResult varNumber
Set "#apm.num=!%~2!"
For %%a in (0 1 2 3 4 5 6 7 8 9 A B C D E F) Do (Set
"#apm.num=!#apm.num:%%a=%%a !")
Set /A "%~1=0"
For %%a in (!#apm.num!) Do (Set /A "%~1+=1")
Set "#apm.num="
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.buildNum which
For %%a in (0 1 2 3 4 5 6 7 8 9 A B C D E F) Do (Set "%1.t=!%1.t:%%a=%%a !")
Set "%1="
Set "%1.len=0"
For %%a in (!%1.t!) Do (Set "%1=%%a!%1!" & Set /A "%1.len+=1")
Set "%1.t=!%1.t: =!"
Set /A "%1.i=%1.len-1"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.longdiv
Set "#T=!#A.t!"
Set "#D="
Set "#C="
Set "#Z="
Set "#i=0"
For %%a in (0 1 2 3 4 5 6 7 8 9) Do Set "#T=!#T:%%a=%%a !"
Set "#len=0"
For %%a in (!#T!) Do (
Set "#D=!#D!%%a"
If "!#D!" EQU "0" Set "#D="
Set "#Z=!#Z!0"
Set /A "#i+=1"
Call :apm.length #len #D
If !#len! GTR 10 (Goto :apm./)
If !#len! EQU 10 (
If "!#D!" GTR "2147483647" (Goto :apm./)
)
If !#D! GEQ !#B.t! (
Set /A "#r=#D %% #B.t, #n=#D/#B.t"
Set "#n=!#Z!!#n!"
For /L %%i in (!#i!, 1, !#i!) Do Set "#C=!#C!!#n:~-%%i!"
Set "#Z="
Set "#i=0"
If "!#r!" EQU "0" (Set "#D=") Else (Set "#D=!#r!")
)
)
Set "#D=!#C!!#Z!"
For %%a in (0 1 2 3 4 5 6 7 8 9) Do Set "#D=!#D:%%a=%%a !"
Set "#C="
For %%a in (!#D!) Do (
Set "#C=!#C!%%a"
if !#C! EQU 0 Set "#C="
)
If NOT DEFINED #C (
Set "#C=0"
) Else (
Set "#C=!#C!"
)
If DEFINED #Q Call :apm #C=!#Q! + !#C!
Set "#C=!#c!"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm./
If NOT DEFINED #B.greater (
If "!#B!" EQU "1" (
Set "#C=!#A.t!"
Goto :EOF
)
If "!#op!" EQU "/" (
If !#B.len! LEQ 10 (
If !#B.len! LSS 10 (Goto :apm.longDiv)
If "!#B.t!" LEQ "2147483647" (Goto :apm.longDiv)
)
)
If "!#B:~0,1!" EQU "0" (
Set "#A.t=!#A.t:~0,-1!"
Call :apm.buildNum #A
Set "#B.t=!#B.t:~0,-1!"
Call :apm.buildNum #B
If !#B.len! EQU 0 (Set "#C=!#A!" & Goto :apm.exit)
Goto :apm./
)
If NOT DEFINED #Guess Set "#Guess=0"
Set /A "#G=#A.len/#B.len"
If !#G! LSS !#A.len! (
Set /A "#T=#G"
For /L %%i in (2 1 !#A.len!) Do (Call :apm #G=!#G! * !#T!)
Call :apm #Guess=!#Guess! + !#G!
Set "#Q=!#Guess!"
Call :apm #G=!#G! * !#B.t!
Call :apm #A.t=!#A.t! - !#G!
Call :apm.buildNum #A
If !#A.len! LSS !#B.len! (
Set /A "#C.len=#B.len"
) Else (
Set /A "#C.len=#A.len"
)
Set /A "#A.i=#A.len-1, #C.i=#C.len-1"
Call :apm.findGreater
Goto :apm./
)
Call :apm #Q=!#Q! + 1

REM Do #D=#C=#A-#B
Set "#C="
Set "#D="
For /L %%i in (0, 1, !#C.i!) Do (
Set "#nA=!#A:~%%i,1!"
Set "#nB=!#B:~%%i,1!"
Set /a "#n=#nA+9-#nB"
Set "#C=!#C! !#n!"
)
Set /A "#carry=1"
For %%a in (!#C!) Do (
Set /A "#n=%%a+#carry, #carry=0"
If !#n! GEQ 10 Set /A "#n-=10, #carry=1"
Set "#D=!#n! !#D!"
)
Set "#A="
Set /A "#A.len=0"
For %%a in (!#D!) Do (
Set "#A=%%a!#A!"
if "!#A!" EQU "0" (
Set "#A="
) Else (
Set /A "#A.len+=1"
)
)
If !#A.len! LSS !#B.len! (
Set /A "#C.len=#B.len"
) Else (
Set /A "#C.len=#A.len"
)
Set /A "#A.i=#A.len-1, #C.i=#C.len-1"
If DEFINED #A (
Call :apm.findGreater
If NOT DEFINED #B.greater (
Goto :apm./
)
)
)
Set "#C=!#Q!"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.-
Set "#C="
:: 10s complement subtraction.
For /L %%i in (0, 1, !#C.i!) Do (
Set "#nA=!#A:~%%i,1!"
Set "#nB=!#B:~%%i,1!"
Set /a "#n=#nA+9-#nB"
Set "#C=!#C! !#n!"
)
Set /A "#carry=1"
For %%a in (!#C!) Do (
Set /A "#n=%%a+#carry, #carry=0"
If !#n! GEQ 10 Set /A "#n-=10, #carry=1"
Set "#D=!#n! !#D!"
)
REM If !#carry! EQU 1 Set "#D=1!#D!"
set "#C=!#D!"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.+
Set "#C="
For /L %%i in (0, 1, !#C.i!) Do (
Set "#nA=!#A:~%%i,1!"
Set "#nB=!#B:~%%i,1!"
Set /a "#n=#nA+#nB+#carry"
Set /A "#carry=0"
If !#n! GEQ 10 (Set /A "#n-=10, #carry=1")
Set "#C=!#n!!#C!"
)
If !#carry! EQU 1 Set "#C=1!#C!"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.*
Set /A "#carry=0"
Set "#z="
For /L %%b in (0, 1, !#B.i!) Do (
Set "#L=!#z!"
For /L %%a in (0, 1, !#A.i!) Do (
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.swap
REM A is less than B so swap them.
(
Set "#A=%#B%"
Set "#B=%#A%"
Set "#A.len=%#B.len%"
Set "#B.len=%#A.len%"
Set "#A.i=%#B.i%"
Set "#B.i=%#A.i%"
Set "#A.neg=%#B.neg%"
Set "#B.neg=%#A.neg%"
Set "#A.greater=%#B.greater%"
Set "#B.greater=%#A.greater%"
)
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.hexToDec
SetLocal EnableExtensions EnableDelayedExpansion
Set "#var=%1"
Set "#R=%2"
Set "#i=1"
Set "#C=0"
:apm.hexToDecLoop
Set /A "#H=0x!#R:~-4!"
Set "#R=!#R:~0,-4!"
Call :apm #H=!#H! * !#i!
Call :apm #C=!#C! + !#H!
Call :apm #i=!#i! * 65536
If DEFINED #R Goto :apm.hexToDecLoop
EndLocal & Set "%#var%=%#C%"
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::
:apm.OR
:apm.AND
Set "#hex=0123456789ABCDEF"
If /I "!#op!" EQU "AND" (Set "#=&") Else (Set "#=|")
Set "#C="
For /L %%a in (0, 1, !#C.i!) Do (
Set /A "#n=0x0!#A:~%%a,1! !#! 0x0!#B:~%%a,1!"
For /L %%i in (!#n!, 1, !#n!) Do Set "#C=!#hex:~%%i,1!!#C!"
)
set "#C.hex=0x!#C!"
Call :apm.hexToDec #C !#C!
Goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::

Dr J R Stockton

unread,
Apr 2, 2012, 1:59:05 PM4/2/12
to
In alt.msdos.batch.nt message <jl9ib8$ru2$1...@news.albasani.net>, Sun, 1
Apr 2012 05:39:53, Frank P. Westlake <frank.w...@gmail.net> posted:
I can do it in any base from 2 to 16; to convert quantities-in-hand and
future arithmetic to base N, one need only insert N bas at the required
point in the RPN code supplied on the command-line or in a file. So it
is possible to calculate in base 16 for speed, yet to output in any
other in-range base, with or without thousands separators.

Division just uses long division as was taught in schools; square root
uses a corresponding method.

--

Bob

unread,
Apr 2, 2012, 10:36:44 PM4/2/12
to
Frank P. Westlake typed the following on 3/28/2012 11:41 AM:
> :: apm.cmd
> @Goto :main
>
> This is the beginning of an arbitrary precision math subroutine
<sniped code>

Greetings Frank,
I must say your batch file skills have made arithmetical progress since
your last script:

@Echo OFF
DIR

I did feed your script several numbers and varying operators and all
worked as expected. I'll tuck the code away for future use.

Thank you and best wishes!

..arbitrary precision math..<grin>


frank.w...@gmail.com

unread,
Apr 3, 2012, 5:22:21 PM4/3/12
to
From jeb :
^ Hi Frank, you have to much time :-)

Not enough. I've moved to my summer camp so it might
have to sleep until November.

^ ..you could improve the maximum allowed length of the
^ numbers, if
^ you could also use variable names as operands.
^ This would increase the max length to ~8100 digits,
^ important when
^ using additions/subtractions.

That's a lot of digits. I'll think about making that change after I add binary shifts (<<, >>). Wouldn't file-based variables increase the maximum even more?

FOR ... %%a in (fileVarA) do (
FOR ... %%b in (fileVarB) do (
rem Add digits here.

I would have to immediately store each digit of the
result into a file. But scripts are slow so that is
probably too much time.

Frank

frank.w...@gmail.com

unread,
Apr 3, 2012, 5:42:53 PM4/3/12
to
From Bob :
>I did feed your script several numbers and varying
>operators and all worked as expected.

Division needs more testing. If the divisor is <= 07FFFFFF then I use long division, otherwise I use a repetitive 'guess and check' I came up with followed by division by subtraction. It's the 'guess and check' that needs testing. I'll run a lot of tests if I get time on the laptop this week (I'm on a 5-inch tablet).

Frank

jeb

unread,
Apr 4, 2012, 1:47:35 PM4/4/12
to
I suppose 8190 is a reasonable limit, as it's hard to handle larger
"objects" with batch,
variables can't hold more than 8192 characters
and even to output more than 8190 characters doesn't work.

You could split the numbers over an array (as I do in my library), but
even then I would limit the total digit length to 8190.

Btw. With a FOR loop you can read more than 8190 characters into a
parameter, it seems to be supported nearly "unlimited" length,
but I don't know any way to access the content of a long size param!

jeb

frank.w...@gmail.com

unread,
Apr 4, 2012, 7:16:20 PM4/4/12
to
A couple of patches.

PATCH 1: In the ':apm' heading change

Version: 2012-03-30

to

Version: 2012-04-01


PATCH 2: In the subroutine ':apm.exit" change

EndLocal & Set "%#var%=%#ans%"

to

EndLocal & Set "%#var%=%#ans%" & Set "%#var%.hex=%#C.hex%"

Frank
--
Please pardon me if my comments are late: I read and
write mail at home and transfer it away from home.

foxidrive

unread,
Apr 5, 2012, 6:58:06 AM4/5/12
to
On 5/04/2012 09:16, frank.w...@gmail.com wrote:
> A couple of patches.
>
> PATCH 1: In the ':apm' heading change
>
> Version: 2012-03-30
>
> to
>
> Version: 2012-04-01

Is this the April fools version that formats your hard drive?

>
> PATCH 2: In the subroutine ':apm.exit" change
>
> EndLocal & Set "%#var%=%#ans%"
>
> to
>
> EndLocal & Set "%#var%=%#ans%" & Set "%#var%.hex=%#C.hex%"
>
> Frank
> --
> Please pardon me if my comments are late: I read and
> write mail at home and transfer it away from home.


--
Mic

frank.w...@gmail.com

unread,
Apr 7, 2012, 8:55:21 AM4/7/12
to
From jeb:
>...variables can't hold more than 8192
>characters...

I did some testing with Windows NT4 Server ten to twelve
years ago and I was able to stuff almost 32k into the
environment, but I don't recall if I needed to
distribute that among multiple variables; and that was
from a compiled executable -- I didn't test a script.

If files are used I think it would be necessary to store
the digits as separate lines so that long numbers can be
read with FOR; otherwise the line length exceeds FOR's
ability. For example, to store digits:

(ECHO(%thisDigit%)>>answer

Then to use the digits in arithmetic:

FOR /F %%a in ('TYPE answer') Do something with digits (%%a) one at a time...

Or to display the answer:

FOR /F %%a in ('TYPE answer') Do SET /P "=%%a"<NUL:

>You could split the numbers over an array (as I do in
my
>library), but even then I would limit the total digit
>length to 8190.

If you publish your library here, or tell us where it
is, I'll stop work on mine. My primary intent has been
to give people arithmetic and binary operations on
64-bit integers, and secondarily to not limit the
integers to 64-bits. Does your library provide that? I'm
not really interested in arbitrary precision; it just
seems to be a good thing to do while I'm doing 64-bit
operations.

Instead of completely stoping my work if your library is
available, I might convert it to strict 64-bit
operations -- unless your library also allows the
integer size to be selected.
0 new messages