2016-07-25 2 views
1

J'ai travaillé sur une calculatrice de polygones Batch et j'ai eu un problème. J'ai besoin de multiplier 2 variables, mais parfois il retourne un nombre négatif si les deux variables positives sont grandes. Voici un exemple: 999999*999999 renvoie -729379967.Le fichier batch multiplie les variables positives retourne un nombre négatif

Code va ci-dessous:

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 

Il semblait que la commande

Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu 

ne calcule pas correctement.

+1

C'est une des deux seules situations où je recommanderai jamais utiliser PowerShell sur une question avec le [fichier batch] marque. – SomethingDark

+0

http://stackoverflow.com/questions/38579524/how-do-i-use-basic-batch-math-commands-to-come-up-with-decimal-answers/38579735#38579735 pour une façon d'utiliser le double mathématiques flottantes de précision. –

+0

@Noodles Merci pour la solution, j'ai déjà signalé le problème. – SteveFest

Répondre

1

Comme d'autres déjà souligné, un natif signé prend en charge Arithmétique entiers seulement 32 bits.

Le code suivant constitue un travail autour de la multiplication de nombres non négatifs supérieurs à la limite de 2 − 1 = 2147483647, en utilisant purs commandes (appelons-le 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 

Pour l'utiliser, fournissez les deux nombres à multiplier comme arguments de ligne de commande; par exemple:

multiply.bat 999999 999999 

Le produit obtenu est retourné sur la console:

999998000001 

Si vous fournissez un troisième argument, le produit est affecté à une variable avec ce nom; par exemple:

multiply.bat 999999 999999 SquArea 

Ceci définit la variable SquArea à la valeur résultante. Ce dernier est également toujours renvoyé sur la console.
Pour affecter silencieusement la variable sans sortie de console supplémentaire, redirigez au dispositif nul:

multiply.bat 999999 999999 SquArea > nul 
+0

Vous pourriez être intéressé par [cette solution] (http://stackoverflow.com/a/38579834) ... – aschipfl

1

Batch utilise des entiers 32 bits pour stocker des nombres. Cela leur donne une taille maximale de 2^31 - 1 = 2 147 483 647.

999,999 * 999,999 = 999,998,000,001 qui est plus grand que 2 147 483 647 donc il "enveloppe" et part des négatifs.

Ceci est une limitation du lot bien qu'il existe quelques workarounds.

0

j'ai remarqué en utilisant lot pur, ce serait un peu à très difficile à mettre en œuvre les opérations qui prennent en charge les numéros au-delà de l'entier 32 bits limite. Donc, à la place, j'ai limité les nombres, afin qu'ils ne débordent pas lorsqu'ils sont multipliés.

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