Batch file multiply positive variables return a negative number

三世轮回 提交于 2019-12-11 06:35:20

问题


I've been working on a Batch polygon area calculator and I got a problem. I need to multiply 2 variables, but sometimes it return a negative number if the two positive variables are large.

Here's an example: 999999*999999 returns -729379967.

Code goes below:

REM Calc square area
:PolySqu
Cls
Echo                                        Polygon Area Calculator 
For /L %%P In (1,1,57) Do Echo.
Set /P "InputPolygonCalSqu=Enter one of the line's length in cm :"


Set /A SquArea=InputPolygonCalSqu * InputPolygonCalSqu


Cls
Echo                                        Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Echo The area of this square is %SquArea% cm2.
Pause
Goto :PolygonCal

It seemed the command

Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu

doesn't calculate properly.


回答1:


As others already pointed out, a batch-file natively supports 32-bit signed integer arithmetics only.

The following code constitutes a work-around for multiplying non-negative numbers greater than the limit of 232 − 1 = 2147483647, using pure batch-file commands (let us call it multiply.bat):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define arguments here:
set "NUM1=%~1"
set "NUM2=%~2"
set "NUM3=%~3"
set "NUM4=%~4"
if defined NUM1 set "NUM1=%NUM1:"=""%
if defined NUM2 set "NUM2=%NUM2:"=""%
if defined NUM3 set "NUM3=%NUM3:"=%
call :VAL_ARGS NUM1 NUM2 NUM4 || exit /B 1

rem // Define constants here:
set /A "DIG=4" & set "PAD="
setlocal EnableDelayedExpansion
for /L %%J in (1,1,%DIG%) do set "PAD=!PAD!0"
endlocal & set "PAD=%PAD%"

rem // Determine string lengths:
call :STR_LEN LEN1 NUM1
call :STR_LEN LEN2 NUM2
set /A "LEN1=(LEN1-1)/DIG*DIG"
set /A "LEN2=(LEN2-1)/DIG*DIG"
set /A "LIM=LEN1+LEN2+DIG"
for /L %%I in (0,%DIG%,%LIM%) do set /A "RES[%%I]=0"

rem // Perform block-wise multiplication:
setlocal EnableDelayedExpansion
for /L %%J in (0,%DIG%,%LEN2%) do (
    for /L %%I in (0,%DIG%,%LEN1%) do (
        set /A "IDX=%%I+%%J"
        if %%I EQU 0 (set "AUX1=-%DIG%") else (
            set /A "AUX1=%DIG%+%%I" & set "AUX1=-!AUX1!,-%%I"
        )
        if %%J EQU 0 (set "AUX2=-%DIG%") else (
            set /A "AUX2=%DIG%+%%J" & set "AUX2=-!AUX2!,-%%J"
        )
        for /F "tokens=1,2" %%M in ("!AUX1! !AUX2!") do (
            set "AUX1=!NUM1:~%%M!" & set "AUX2=!NUM2:~%%N!"
        )
        call :NO_LEAD0 AUX1 !AUX1!
        call :NO_LEAD0 AUX2 !AUX2!
        set /A "RES[!IDX!]+=AUX1*AUX2"
        set /A "NXT=IDX+DIG, DIT=DIG*2"
        for /F "tokens=1,2,3" %%M in ("!IDX! !NXT! !DIT!") do (
            set "AUX=!RES[%%M]:~-%%O,-%DIG%!"
            set /A "RES[%%N]+=AUX"
            set "RES[%%M]=!RES[%%M]:~-%DIG%!"
            call :NO_LEAD0 RES[%%M] !RES[%%M]!
        )
    )
)

rem // Build resulting product:
set "RES=" & set "AUX="
for /L %%I in (0,%DIG%,%LIM%) do (
    set /A "RES[%%I]+=AUX"
    set /A "NXT=%%I+DIG"
    for /L %%J in (!NXT!,%DIG%,!NXT!) do (
        set "AUX=!RES[%%I]:~-%%J,-%DIG%!"
    )
    set "RES[%%I]=%PAD%!RES[%%I]!"
    set "RES=!RES[%%I]:~-%DIG%!!RES!"
)
endlocal & set "RES=%RES%"
call :NO_LEAD0 RES %RES%

rem // Return resulting product:
echo(%RES%
if defined NUM3 (
    endlocal
    set "%NUM3%=%RES%"
) else (
    endlocal
)
exit /B


:NO_LEAD0  rtn_var  val_num
rem // Remove leading zeros from a number:
for /F "tokens=* delims=0" %%Z in ("%~2") do (
    set "%~1=%%Z" & if not defined %~1 set "%~1=0"
)
exit /B 0


:STR_LEN  rtn_length  ref_string
rem // Retrieve length of string:
setlocal EnableDelayedExpansion
set "STR=!%~2!"
if not defined STR (set /A LEN=0) else (set /A LEN=1)
for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if defined STR (
        set "INT=!STR:~%%L!"
        if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!"
    )
)
endlocal & set "%~1=%LEN%"
exit /B 0


:VAL_ARGS  ref_arg1  ref_arg2  ref_arg3
rem // Check arguments for validity:
if not defined %~1 >&2 echo ERROR: too few arguments given! & exit /B 1
if not defined %~2 >&2 echo ERROR: too few arguments given! & exit /B 1
if defined %~3 >&2 echo ERROR: too many arguments given! & exit /B 1
(call echo "%%%~1%%" | > nul findstr /R /C:"^\"[0-9][0-9]*\" $") || (
    >&2 echo ERROR: argument 1 is not purely numeric! & exit /B 1
)
(call echo "%%%~2%%" | > nul findstr /R /C:"^\"[0-9][0-9]*\" $") || (
    >&2 echo ERROR: argument 2 is not purely numeric! & exit /B 1
)
exit /B 0

To use it provide the two numbers to multiply as command line arguments; for instance:

multiply.bat 999999 999999

The resulting product is returned on the console:

999998000001

If you provide a third argument, the product is assigned to a variable with that name; for example:

multiply.bat 999999 999999 SquArea

This sets variable SquArea to the resulting value. The latter is also still returned on the console.
To silently assign the variable without any additional console output, redirect it to the nul device:

multiply.bat 999999 999999 SquArea > nul



回答2:


Batch uses 32bit integers for storing numbers. This gives them a maximum size of 2^31 - 1 = 2,147,483,647.

999,999 * 999,999 = 999,998,000,001 which is larger than 2,147,483,647 therefore it "wraps" and starts from negatives.

This is a limitation of batch although there are some workarounds.




回答3:


This might be useful

Can batch files not process large numbers?

The largest number batch can take is 2^31 - 1 = 2,147,483,647.

So again, it is starting back from negative and giving you that answer..




回答4:


I noticed using pure batch, it would be somewhat to very difficult to implement operations that support the numbers beyond the 32-bit integer limit. So instead, I limited the numbers, so they won't overflow when multiplied.

REM Calc Square Area
:PolySqu
Cls
Echo                       Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Set /P "InputPolygonCalSqu=Enter one of the line's length in cm [Less then 40000] :"
If %InputPolygonCalSqu% GTR 40000 Goto :PolySqu

Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu


Cls
Echo                       Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Echo The area of this square is %SquArea% cm2.
Pause
Goto :PolygonCal 


来源:https://stackoverflow.com/questions/38570714/batch-file-multiply-positive-variables-return-a-negative-number

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!