【问题标题】:Batch file multiply positive variables return a negative number批处理文件乘以正变量返回负数
【发布时间】:2016-07-25 14:39:45
【问题描述】:

我一直在研究 Batch 多边形面积计算器,但遇到了问题。 我需要将两个变量相乘,但如果两个正变量很大,有时它会返回一个负数。

这是一个示例:999999*999999 返回-729379967

代码如下:

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

似乎是命令

Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu

计算不正确。

【问题讨论】:

  • 这是我建议在带有 [batch-file] 标签的问题上使用 PowerShell 的仅有的两种情况之一。
  • stackoverflow.com/questions/38579524/… 了解使用双精度浮点数学的方法。
  • @Noodles 谢谢你的解决方案,我已经指出了问题。

标签: batch-file multiplication area


【解决方案1】:

正如其他人已经指出的那样, 本身仅支持 32 位有符号整数算术。

以下代码构成了一个解决方法,用于将大于限制的非负数相乘 232 - 1 = 2147483647,使用纯 命令(让我们称之为 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

要使用它,请提供两个要相乘的数字作为命令行参数;例如:

multiply.bat 999999 999999

在控制台上返回结果产品:

999998000001

如果您提供第三个参数,则将产品分配给具有该名称的变量;例如:

multiply.bat 999999 999999 SquArea

这会将变量SquArea 设置为结果值。后者也仍然在控制台上返回。
要在没有任何额外控制台输出的情况下静默分配变量,请将其重定向到 nul 设备:

multiply.bat 999999 999999 SquArea > nul

【讨论】:

【解决方案2】:

Batch 使用 32 位整数来存储数字。这使它们的最大大小为 2^31 - 1 = 2,147,483,647。

999,999 * 999,999 = 999,998,000,001 大于 2,147,483,647,因此它“包裹”并从负数开始。

这是批处理的限制,虽然有一些workarounds

【讨论】:

    【解决方案3】:

    这可能有用

    Can batch files not process large numbers?

    batch 的最大数量是 2^31 - 1 = 2,147,483,647。

    同样,它从否定开始,给你答案..

    【讨论】:

      【解决方案4】:

      我注意到使用纯批处理,实现支持超过 32 位整数限制的数字的操作会有些困难。所以相反,我限制了数字,所以它们在相乘时不会溢出。

      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 
      

      【讨论】:

        猜你喜欢
        • 2017-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-20
        • 2023-03-06
        • 1970-01-01
        • 2018-03-25
        • 2010-10-23
        相关资源
        最近更新 更多