Floating point division in a batch file - batch-file

I need to do a floating-point division in a dos batch.
I didn't find a way to do it. Something like this :
SET /A Res=10/3
returns a integer number.
Is it possible to do it ?

I know this is a very old topic, but I can't found a simple Batch method in all previous answers, so I post here a pure Batch solution that is very simple to use.
Perform operations using fixed point arithmetic in Batch is simple. "Fixed point" means that you must set a number of decimals in advance and keep it throughout the operations. Add and subtract operations between two Fixed Point numbers are performed directly. Multiply and division operations requires an auxiliary variable, that we can call "one", with the value of 1 with the right number of decimals (as "0" digits). After multiply, divide the product by "one"; before division, multiply the dividend by "one". Here it is:
#echo off
setlocal EnableDelayedExpansion
set decimals=2
set /A one=1, decimalsP1=decimals+1
for /L %%i in (1,1,%decimals%) do set "one=!one!0"
:getNumber
set /P "numA=Enter a number with %decimals% decimals: "
if "!numA:~-%decimalsP1%,1!" equ "." goto numOK
echo The number must have a point and %decimals% decimals
goto getNumber
:numOK
set numB=2.54
set "fpA=%numA:.=%"
set "fpB=%numB:.=%"
set /A add=fpA+fpB, sub=fpA-fpB, mul=fpA*fpB/one, div=fpA*one/fpB
echo %numA% + %numB% = !add:~0,-%decimals%!.!add:~-%decimals%!
echo %numA% - %numB% = !sub:~0,-%decimals%!.!sub:~-%decimals%!
echo %numA% * %numB% = !mul:~0,-%decimals%!.!mul:~-%decimals%!
echo %numA% / %numB% = !div:~0,-%decimals%!.!div:~-%decimals%!
For example:
Enter a number with 2 decimals: 3.76
3.76 + 2.54 = 6.30
3.76 - 2.54 = 1.22
3.76 * 2.54 = 9.55
3.76 / 2.54 = 1.48

Batch files as such do not support the floating point arithmetic. However, this article suggests a workaround that uses an external script file to do calculations. The script file should use some sort of eval function to evaluate the expression passed as an argument and return the result. Here's a sample VBScript file (eval.vbs) that does this:
WScript.Echo Eval(WScript.Arguments(0))
You can call this external script from your batch file, specify the expression to be evaluated and get the result back. For example:
#echo off
for /f %%n in ('cscript //nologo eval.vbs "10/3"') do (
set res=%%n
)
echo %res%
Of course, you'll get the result as a string, but it's better than nothing anyway, and you can pass the obtained result to the eval script as part of another expression.

According to this reference, there is no floating point type in DOS batch language:
Although variables do exist in the DOS batch programming language, they are extremely limited. There are no integer, pointer or floating point variable types, only strings.
I think what you are trying to do will be impossible without implementing your own division scheme to calculate the remainder explicitly.

I recently came across this batch file to compute an approximation of Pi.
There is a DivideByInteger label that might be useful to you: Stupid-Coding-Tricks-A-Batch-of-Pi
It uses a set of MaxQuadIndex variables, each containing a four-digit number (quadruple), in order to store the entire result. The code allows division by an integer between 1 and 10000, inclusive.
:DivideByInteger
if defined PiDebug echo.DivideByInteger %1 %2
set /a DBI_Carry = 0
for /L %%i in (!MaxQuadIndex!, -1, 0) do (
set /a DBI_Digit = DBI_Carry*10000 + %1_%%i
set /a DBI_Carry = DBI_Digit %% %2
set /a %1_%%i = DBI_Digit / %2
)
goto :EOF
A Print label is also available…

try this
SETLOCAL EnableExtensions EnableDelayedExpansion
call :calc_ 1 (99-(100*5/100^)^)
echo !calc_v!
goto :EOF
:calc_
set scale_=1
set calc_v=
for /l %%i in (1,1,%1) do set /a scale_*=10
set /a "calc_v=!scale_!*%2"
set /a calc_v1=!calc_v!/!scale_!
set /a calc_v2=!calc_v!-!calc_v1!*!scale_!
set calc_v=!calc_v1!.!calc_v2!
goto :EOF
just change
call :calc_ decimalpoint equataion
in the example
decimalpoint is 1
equataion is (99-(100*5/100^)^) ;make sure if you use () that you insert ^ before ) as in ^)
the answer is 94.0
if decimalpoint is 2
and equataion is 22/7 ;π pi
the answer is 3.14

I wrote a pure batch file specifically to do division. It takes the first number you input, and then divides it by the second one, and displays the result with as many decimal points as you specify.
Echo off
cls
if NOT "%3" == "" (
set n1=%1
set n2=%2
set max=%3
goto :begin
)
set counter=2
set n1=1
set n2=1
set ans=
:start
Echo.
Echo. 1 / 2
Echo.
Set /p N1= 1?
set /p N2= 2?
Set /p Max= Out how many Decimal Points?
:begin
set /a TmpAns=%N1%/%N2%
set ans=%TmpAns%.
:: Echo.%ans%.>Answer.txt
<nul set /p "=%Tmpans%."
set /a TmpSub=%N2%*%TmpAns%
set /a N1=%N1%-%TmpSub%
set N1=%N1%0
If NOT "%n1%" == "00" (
if %n1% LSS %N2% (
set N1=%N1%0
set ans=%ans%0
)
) else (
Goto :Finished
)
set count=0
:loop
If "%count%" == "%max%" (
Goto :Finished
)
set /a TmpAns=%N1%/%N2%
set ans=%ans%%TmpAns%
<nul set /p "=%Tmpans%"
set /a TmpSub=%N2%*%TmpAns%
set /a N1=%N1%-%TmpSub%
set N1=%N1%0
If NOT "%n1%" == "00" (
if %n1% LSS %N2% (
set N1=%N1%0
set ans=%ans%0
)
) else (
Goto :Finished
)
set /a count=%count%+1
goto :loop
:finished
cls
Echo.
Echo.
Echo.The Number
Echo.%ans%
Echo.
Echo.
set n1=1
set n2=1
pause
goto :eof
:eof
The answer put into the variable %Ans%. It can also be called with parameters. ("Divide.bat 50 27 5" would give you 50/27 out 5 decimal points.)

Since nowadays PowerShell is present on almost all machines, I would let PowerShell do the math and return the result to the batch.
Example:
set divident=10
set divisor=3
for /f "delims=" %%a in ('powershell -Command %divident%/%divisor%') do set result=%%a
#echo %result%
Explanation:
Input variables: Use set variables to define divident and divisor.
Calling powershell and assign result to a batch variable: for /f "delims=" %%a in ('powershell -Command ...) do set result=%%a (you may also check here: How to put a single PowerShell output string into a cmd variable?)
Note the above code will only work with integer input variables.
To support floating point input variables, we need to send the variables as strings inside quotations ("%variable%") and convert the strings within PowerShell back to Double, otherwise batch would interpret the commas as delimiters and PowerShell could not interpret the numbers.
Example:
set divident=10,5
set divisor=3,4
for /f "delims=" %%a in ('powershell -Command [convert]::ToDouble^(\"%divident%\"^)
/[convert]::ToDouble^(\"%divisor%\"^)') do set result=%%a
#echo %result%
Explanation:
Note in PowerShell you would do this like [convert]::ToDouble("10,5")/[convert]::ToDouble("3,5"). However in batch we need to escape the quotes using backslash, and we also need to add a "^" sign before and after the quoted parts: [convert]::ToDouble^("%divident%"^)/[convert]::ToDouble^("%divisor%"^)

If you're running in a command shell on Windows (rather than DOS), you can use VBScript to evaluate complex expressions including floating point math for you.
I have written a small helper library you can call to do this.
EvalBat Library on GitHub

Related

Command prompt script to subtract variables

I know there are many arithmetic questions on here, but I have not found the specific answer to my question. I have a file with two values in it, the first always higher than the second. Today, the txt file has:
21.04
20.94
What I am trying to do is, via a batch file, subtract the second number from the first, and then insert than on a new line. Any assistance is appreciated.
Just incorporate powershell into the batch file.
to test from cmd:
#for /f %i in ('powershell 21.04 - 20.94') do #echo %i
So you can build a very basic calculator rather easily.
set /p "first=Enter first number: "
set /p "second=Enter Second Number: "
set /p "function=Select Function(+-/): "
powershell %first% %function% %second%
And offcourse you can use a for loop to assign the value to a variable should you want to use it elsewhere in your batch file.
#echo off
set /p "first=Enter first number: "
set /p "second=Enter Second Number: "
set /p "function=Select Function(+-/): "
for /f %%i in ('powershell %first% %function% %second%') do set "result=%%i"
echo %result%
in a batch-file you double the % in meta variables to %%i
Assuming file is called math.txt
#echo off
setlocal enabledelayedexpansion
set cnt=1
for /f "usebackq" %%i in ("d:\math.txt") do (
set var!cnt!=%%i
set /a cnt+=1
)
(powershell %var1% - %var2%)>output.txt
pause
This method works with numbers up to 9 total digits (and any number of decimals) as long as the input numbers have the same number of decimals:
#echo off
setlocal EnableDelayedExpansion
rem Read two numbers
( set /P "num1=" & set /P "num2=" ) < test.txt
rem Adjust *two* numbers for given decimals
set "decimals=2"
for %%i in (1 2) do (
set "num%%i=!num%%i:.=!"
for /L %%d in (1,1,%decimals%) do if "!num%%i:~0,1!" equ "0" set "num%%i=!num%%i:~1!"
)
rem Subtract second number from the first
set /A "result=num1 - num2"
rem Adjust result for given number of decimals
for /L %%d in (1,1,%decimals%) do if "!result:~%decimals%!" equ "" set "result=0!result!"
rem Output result with decimals
echo !result:~0,-%decimals%!.!result:~-%decimals%!
I think this works for reasonably sized numbers:
#echo off
rem read values from file specified as command line parameter
(
set /p value1=
set /p value2=
)<%1
rem split first value into whole and fractional parts
for /f "tokens=1,2 delims=." %%a in ("%value1%") do (
set beforedot1=%%a
set afterdot1=%%b
)
rem reconstruct first value as fixed point number
set afterdot1=%afterdot1%00000
set afterdot1=%afterdot1:~0,6%
set value1=%beforedot1%%afterdot1%
rem split second value into whole and fractional parts
for /f "tokens=1,2 delims=." %%a in ("%value2%") do (
set beforedot2=%%a
set afterdot2=%%b
)
rem reconstruct second value as fixed point number
set afterdot2=%afterdot2%00000
set afterdot2=%afterdot2:~0,6%
set value2=%beforedot2%%afterdot2%
rem subtract values
set /a diff=value1-value2
rem convert fixed point value back
if "%diff:~0,-6%" == "" (
set diff=0.%diff:~-6%
) else (
set diff=%diff:~0,-6%.%diff:~-6%
)
rem remove trailing zeros
:loop
if "%diff:~-1%" == "0" (
set diff=%diff:~0,-1%
goto :loop
)
echo %diff%

Why are mathematical operations and output of environment variables in an ELSE branch not working as expected?

I am trying to use nested if-else statements in order to implement or operator.
The problem is that the code only works outside the last nested else statement and I can't figure out why.
I added some notes marked with // that are not actually in the script to help you get a clue of what I am trying to do.
Here is my batch script:
:computerMakePick
setLocal
set /a currentNumber= 15
set /a addOne=%currentNumber%+1
set /a addTwo=%currentNumber%+2
//the next segment implements OR operator for two conditions using nested if-else statement
if %addOne% == 7 ( //checking first condition.
echo Computer chose: %addOne%
set /a currentNumber= %addOne%
)else (
if %addTwo% == 8 ( // now checking: OR second condition
echo Computer chose: %addTwo%
set /a currentNumber= %addTwo%
)else ( // if not both of the above then do this. NOW this section below doesn't work
set /a bottomlimit= 1
set /a upperlimit= 2
set /a limit=%upperlimit%-%bottomlimit%+1
set /a randomChoice= %bottomlimit% + %RANDOM% %% %limit%
set /a currentNumber= %currentNumber%+%randomChoice%
echo Computer chose: %currentNumber%
)
)
endLocal & set /a currentNumber= %currentNumber%
goto :eof
If I take the last else section to outside like this below, then it works:
:computerMakePick
setLocal
set /a currentNumber= 15
set /a addOne=%currentNumber%+1
set /a addTwo=%currentNumber%+2
//the next segment implements OR operator for two conditions using nested if-else statement
if %addOne% == 7 ( //checking first condition.
echo Computer chose: %addOne%
set /a currentNumber= %addOne%
)else (
if %addTwo% == 8 ( // now checking: OR second condition
echo Computer chose: %addTwo%
set /a currentNumber= %addTwo%
)else (
echo. // need to put something in here or else it doesn't work.
) // could also delete this last else-statment but it doesn't matter
)
//now this below works fine. and I don't understand why under second-else section it doesn't
set /a bottomlimit= 1
set /a upperlimit= 2
set /a limit=%upperlimit%-%bottomlimit%+1
set /a randomChoice= %bottomlimit% + %RANDOM% %% %limit%
set /a currentNumber= %currentNumber%+%randomChoice%
echo Computer chose: %currentNumber%
endLocal & set /a currentNumber= %currentNumber%
goto :eof
By saying it's not working I mean if I print the values of each variable: bottomlimit, upperlimit, limit, etc. when they are defined inside the second else statement, for example for the command line echo value of limit is = %limit% I get blanks (nothing).
Why is this happening and how can I fix it to work inside the second else statement?
Use the following code:
#echo off
:computerMakePick
setLocal
set "currentNumber=15"
set /a addOne=currentNumber + 1
set /a addTwo=currentNumber + 2
rem // the next segment implements OR operator for two conditions using nested if-else statement
if %addOne% == 7 ( rem // checking first condition.
echo Computer chose: %addOne%
set "currentNumber=%addOne%"
) else if %addTwo% == 8 ( rem // now checking: OR second condition
echo Computer chose: %addTwo%
set "currentNumber=%addTwo%"
) else ( rem // if not both of the above then do this. NOW this section below doesn't work
set "bottomlimit=1"
set "upperlimit=2"
set /a limit=upperlimit - bottomlimit + 1
set /a randomChoice=bottomlimit + %RANDOM% %% limit
set /a currentNumber+=randomChoice
setlocal EnableDelayedExpansion
echo Computer chose: !currentNumber!
endlocal
)
endLocal & set "currentNumber=%currentNumber%"
goto :EOF
Environment variables are always of type string. So even on using integers, the numbers are stored in memory as strings and not as integers. Therefore don't use set /a variable=number if there is no real reason to do so as this results in converting number from string to integer for the arithmetic expression, and converting it back from integer to string for assigning the result of this arithmetic expression to environment variable.
Usage of environment variable expansion within an arithmetic expression which is the string after set /a is usually nonsense as Windows command interpreter automatically interprets each string not being a number or an operator as name of an environment variable whose current value has to be converted to an integer for evaluation of the expression.
Yes, whether immediate nor delayed expansion is needed within an arithmetic expression even when the set /a command line is within a command block.
And in batch files // is not a comment, use command REM and take into account that Windows command interpreter first parses the lines with command REM and then executes the command if there is no syntax error, see %~ in REM statement.
For more details:
Run in a command prompt window set /? and really read carefully everything of output help.
If you use any of the logical or modulus operators, you will need to
enclose the expression string in quotes. Any non-numeric strings in the
expression are treated as environment variable names whose values are
converted to numbers before using them. If an environment variable name
is specified but is not defined in the current environment, then a value
of zero is used. This allows you to do arithmetic with environment
variable values without having to type all those % signs to get their
values.
Read the answer Why is no string output with 'echo %var%' after using 'set var = text' on command line?
Read the answer on IF ELSE syntax error within batch file?
Variables within the else-statement are not expanded. Use setlocal enabledelayedexpansion instead and denote your variables with exclamation marks:
:computerMakePick
setLocal enabledelayedexpansion
set /a currentNumber= 15
set /a addOne=%currentNumber%+1
set /a addTwo=%currentNumber%+2
if %addOne% == 7 (
echo Computer chose: %addOne%
set /a currentNumber= %addOne%
)else (
if %addTwo% == 8 (
echo Computer chose: %addTwo%
set /a currentNumber= %addTwo%
)else (
set /a bottomlimit= 1
set /a upperlimit= 2
set /a limit=!upperlimit!-!bottomlimit!+1
set /a randomChoice= !bottomlimit! + !RANDOM! %% !limit!
set /a currentNumber= !currentNumber!+!randomChoice!
echo Computer chose: !currentNumber!
)
)
endLocal & set /a currentNumber= %currentNumber%
goto :eof

Update running unsigned long long sum in bat file

Is there a simple way to sum two numbers potentially >= 2*31 in a .BAT file?
I have a running sum, and argument %1 that is the name of an existing file.
set sum=4123456789
set fsize=%~z1
I'd like to add fsize to sum. Unfortunately fsize (and sum) can be as tiny as zero or 10's of gigabytes (%~z1 accurately reports >= 2*31 file sizes).
I know a program could do it, and I'll go that route if necessary, but I'd prefer to do it with a few added lines of .BAT logic.
I think the easiest way is to split the summands into two parts – integer and fractional Gigas (multiples of 1000000000), add the respective parts individually, then recombine them. See the following example script, which contains a lot of explanatory remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Initialise variables:
set /A "GIGASUM=0, FRACSUM=0"
:LOOP
rem // Get command line argument:
shift
set "NUM=%~0"
if not defined NUM goto :NEXT
rem // Check number for validity:
(for /F "delims=0123456789" %%N in ("%NUM%") do rem/) && (
echo ERROR: non-numeric characters encountered!
exit /B 1
)
rem // Split number into two parts, integer and fractional Gigas:
set "NUM=000000000%NUM%"
set "GIGA=%NUM:~,-9%" & set "FRAC=%NUM:~-9%"
rem // Remove leading zeros from integer Gigas:
for /F "tokens=* delims=0" %%N in ("%GIGA%") do set "GIGA=%%N"
rem // Sum up fractional Gigas and determine carry:
set /A "FRACSUM+=(1%FRAC%-1000000000)"
set "CARRY=%FRACSUM:~,-9%" & set "FRACSUM=000000000%FRACSUM%"
set "FRACSUM=%FRACSUM:~-9%"
rem // Sum up integer Gigas and regard carry:
set /A "GIGASUM+=GIGA+CARRY"
rem // Loop back to next summand:
goto :LOOP
:NEXT
rem // Remove leading zeros:
for /F "tokens=* delims=0" %%N in ("%GIGASUM%%FRACSUM%") do set "SUM=%%N"
if not defined SUM set "SUM=0"
rem // Return resulting sum:
echo %SUM%
endlocal
exit /B
The greatest possible sum amounts to 231 * 109 – 1 = 2147483647999999999, an overflow is not detected.
The required logic is not so complicated. Here it is one version:
#echo off
setlocal
:loop
set /p "pair=Enter two numbers separated by plus sign: "
if errorlevel 1 goto :EOF
for /F "tokens=1,2 delims=+" %%a in ("%pair%") do set "num1=%%a" & set "num2=%%b"
set "sum="
set "carry=0"
:nextDigit
set /A sum1=%num1:~-1%+%num2:~-1%+carry
set "sum=%sum1:~-1%%sum%"
set /A carry=sum1/10
set "num1=%num1:~0,-1%"
if not defined num1 set "num1=0"
set "num2=%num2:~0,-1%"
if not defined num2 set "num2=0"
if "%carry%%num1%%num2%" neq "000" goto nextDigit
echo The sum is: %sum%
goto loop

Problems with for loops in batch file

I am trying to create a for loop in batch with some calculation using modulus from 1-100 then return an average for the entire set of numbers. I want to print this average on the screen and to a file called output This is what i have but it doesn't seem to be working correctly. Any suggestions? Thank you kindly.
#echo off
setlocal EnabledDelayedExpansion
for /l %%i in (1,1,100) do (
set /a EXPR = %%i %% 5
set /a EXPR2 = %EXPR+3
set /a TOTAL = TOTAL+%EXPR2
)
set /a AVG = %TOTAL/100
echo Your average is %AVG
echo Your average is %AVG >> output.txt
#echo off
setlocal EnableDelayedExpansion
for /l %%i in (1,1,100) do (
set /a EXPR=%%i %% 5
set /a EXPR2=EXPR+3
set /a TOTAL=TOTAL+EXPR2
)
set /a AVG=TOTAL/100
echo Your average is %AVG%
echo Your average is %AVG%
Is this what you want?
One additional d in EnableDelayedExpansion (btw. set /a works also without delayed expansion). Classical error is to let spaces surrounding variable assignment.In batch scripts result is that the space becomes part of the variable name.Except FOR tokens and command line arguments variables are two side enclosed with %

Batch Calcs.. Not the same as usual

I'm trying to make a desktop binary calculator and can't seem to get values to calculate properly.
http://pastebin.com/embed_js.php?i=mtsJRys8
:: functions for 128
if userValue GEQ 128 (
set bin128 = 1
goto 64
)
if not userValue GEQ 128 (
set bin128 = 0
)
Trying to get the Variable 'bin128' to end with a value 1 or 0
Decimal to binary converter?
I see in your paste you're computing the binary result by comparing the entered number with powers of two. There's a more efficient algorithm for converting dec to bin, and it'll handle numbers from 0 to 2147483647.
#echo off
setlocal
if "%~1"=="" (
echo usage: %~nx0 integer
goto :EOF
)
set /a dec = %~1
:/2
set /a mod = dec %% 2, dec /= 2
set "bin=%mod%%bin%"
if %dec% gtr 0 goto :/2
echo %bin%
I also see paulsm4's comment recommending another (suspicious quote) "real" language. Truthfully, though, not counting languages that already have a direct int-to-bin conversion method, the batch scripting language can actually save you a few steps because all math is integer math, with decimals inherently truncated. This saves you from having to Math.floor or similar on every iteration. This is one situation where batch is better-suited for the task than many other languages.
If you prefer to do the power-of-two comparison method, here's another solution. It loops 31 times regardless of the length of the numeral you supply, but for /L is very efficient. This may actually be faster than the method above for larger numbers for many iterations. Not sure.
#echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo usage: %~nx0 integer
goto :EOF
)
set /a dec = %~1, pow = 1073741824
for /L %%I in (1,1,31) do (
if !dec! geq !pow! (
set "bin=!bin!1"
set /a dec -= pow
) else (
if defined bin set "bin=!bin!0"
)
set /a pow /= 2
)
if not defined bin set bin=0
echo %bin%
And just because I felt like it, here's the same thing but using bitwise operations. This is the most efficient method. (Edit: or at least it was, until Aacini posted his solution.)
#echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo usage: %~nx0 integer
goto :EOF
)
for /L %%I in (0,1,30) do (
set /a "mask = 1 << %%I, bit = ^!^!(%~1 & mask)"
if !mask! gtr %~1 goto break
set "bin=!bit!!bin!"
)
:break
if not defined bin set bin=0
echo %bin%
Efficiency:
I ran a series of tests to determine which method is most efficient. For each method, I sent the output to NUL, looped the script for 1000 iterations, then took an average of the run times over 3 runnings. I did this first with a small value of 5, then a large value of 2147483646. Results:
goto method, input=small: 7.37 seconds
goto method, input=large: 33.77 seconds
powers-of-two method, input=small: 8.38 seconds
powers-of-two method, input=large: 12.44 seconds
bitwise method, input=small: 6.35 seconds
bitwise method, input=large: 11.42 seconds
These results are not surprising, as for /L is generally faster than a goto loop, and bitwise operations occur faster than integer math.
Just to complete rojo's answer, I think that this is the most efficient method to convert a decimal number to binary:
#echo off
setlocal EnableDelayedExpansion
if "%~1"=="" (
echo usage: %~nx0 integer
goto :EOF
)
set "decimal=%~1"
set "binary="
for /L %%i in (1,1,32) do (
set /A "bit=decimal&1, decimal>>=1"
set "binary=!bit!!binary!"
if !decimal! equ 0 goto break
)
:break
echo %binary%
Note that previous method correctly convert negative numbers!
At this post there is a Batch file that can convert very large decimal numbers to binary. The result is stored in a Batch variable, so the binary number may have a maximum of 8 K digits. This means that the maximum decimal number that can be converted is 2^8192 - 1.

Resources