How to count the ones in a binary representation of a given hex in a cmd Batch? - batch-file

i am trying to write a batch file that asks the user to type a hexadcimal number and then counts the number of ones in the binary representation , like if i typed A the program echos 1 .
i have this code that uses a look up table to convert from binary to hex and answered by Aacini here ...
#echo off
setlocal
set "bin=110111101010110110111110111011111100101011111110"
call :bin2hex hex=%bin%
echo hex: %hex%
goto :EOF
:bin2hex hexVar=binValue
setlocal EnableDelayedExpansion
for %%a in (0000-0;0001-1;0010-2;0011-3;0100-4;0101-5;0110-6;0111-7;1000-8;1001-9;1010-A;1011-B;1100-C;1101-D;1110-E;1111-F) do (
for /F "tokens=1,2 delims=-" %%b in ("%%a") do (
set "hextable[%%b]=%%c"
)
)
set "hex="
set "bin=000%~2"
:bin2hexloop
set "hex=!hextable[%bin:~-4%]!%hex%"
set "bin=%bin:~0,-4%"
if defined bin if "%bin:~3%" neq "" goto bin2hexloop
endlocal & set "%~1=%hex%"
goto :EOF
i tried to inverse the way this code works , but it didn't work !
here is my attempt
#echo off
setlocal
set "hex=ABCDEF"
call :hex2bin bin=%bin%
echo : %bin%
pause;
goto :EOF
:hex2bin binVar=hexValue
setlocal EnableDelayedExpansion
for %%a in (0-0000;1-0001;2-0010;3-0011;4-0100;5-0101;6-0110;7-0111;8-1000;9-1001;A-1010;B-1011;C-1100;D-1101;E-1110;F-1111) do (
for /F "tokens=1,2 delims=-" %%b in ("%%a") do (
set "bintable[%%b]=%%c"
)
)
set "bin="
set "hex=000%~16"
:hex2binloop
set "bin=!bintable[%hex:~-4%]!%bin%"
set "hex=%hex:~0,-4%"
if defined hex if "%hex:~3%" neq "" goto hex2binloop
endlocal & set "%~4=%bin%"
goto :EOF
anybody can help me ?

If I understood you correctly, you want not to "convert a hexadecimal number to binary", but to "count the ones that each hex digit have" and accumulate they (for example, for A the number is 1). This way, the solution must work with "The number of ones each hex digit have".
#echo off
setlocal
set "hex=ABCDEF"
call :countOnesInHex ones=%hex%
echo There are %ones% ones in %hex%
pause
goto :EOF
:countOnesInHex ones=hexValue
setlocal
for %%a in (0-0;1-1;2-1;3-2;4-1;5-2;6-2;7-3;8-1;9-2;A-2;B-3;C-2;D-3;E-3;F-4) do (
for /F "tokens=1,2 delims=-" %%b in ("%%a") do (
set "onesIn[%%b]=%%c"
)
)
set ones=0
set "hex=%~2"
:hexCountLoop
set /A ones+=onesIn[%hex:~0,1%]
set "hex=%hex:~1%"
if defined hex goto hexCountLoop
endlocal & set "%1=%ones%"
exit /B

Convert the number to decimal and do the basic math: divide by 2 and sum the remainders.
Do it in chunks of 7 hexadecimal digits because batch file calculations support only 31 bits (2^31-1 or 2147483647 or 0x7FFFFFFF).
set hex=ABCDEFABCDEFABCDEF
set ones=0
:loopchunks
set /a decimal=0x%hex:~0,7%
set hex=%hex:~7%
:loopdigits
set /a ones+=decimal %% 2, decimal/=2
if not %decimal%==0 goto loopdigits
if defined hex goto loopchunks
echo %ones%
Output:
51

#ECHO OFF
SETLOCAL
FOR %%a IN (F37ABD abcdef 123 321 99 100 f11f 0 cafe) DO CALL :mainproc %%a
GOTO :eof
:mainproc
SET hexnum=%1
SET /a count1=0
:loop
SET /a hex1=0x%hexnum:~0,1%
:bitloop
SET /a count1+=%hex1% %% 2
SET /a hex1/=2
IF %hex1% gtr 0 GOTO bitloop
SET hexnum=%hexnum:~1%
IF DEFINED hexnum GOTO loop
ECHO %count1% 1s detected IN %1
GOTO :EOF
The for loop simply assigns the values in the list to the variable %%a in turn and executes the main part of the procedure with a parameter (%1) of that item.
Within the main procedure, initialise hexnum as the number to be analysed and count1 with the accumulated number of 1s
Then set hex1 to 0xstrung before (copy the first digit of hexnum) which will be a hex numeric, 0x0 to 0xf. SInce this is the format for cmd to accept a hex number, it sets hex1 to decimal 0..15
next add (hex1 mod 2) to count, that is 1 or zero if odd/even
next halve hex1. Since cmd calculates in integer mode, the result is truncated, hence 6=>3 and 7+>3
the result is >0, do the next binary digit. repeat until 0.
Toss out the first character of hexnum (assign the substring starting at character 1, given that it starts counting at "character 0")
If hexnum has characters left, repeat for the next hex digit
otherwise, report.
result:
17 1s detected IN F37ABD
17 1s detected IN abcdef
4 1s detected IN 123
4 1s detected IN 321
4 1s detected IN 99
1 1s detected IN 100
10 1s detected IN f11f
0 1s detected IN 0
11 1s detected IN cafe

Related

I need to find the smallest number and print the smallest and the others in bat

i neeed to Write a bat file that accepts three parameters. Of these three parameters, find the smallest one, then display all the numbers from 1 to AND the smallest parameter on the screen.
i tried this but it does not work
#ECHO OFF
set /a c=%1
set /a b=%2
set /a a=%3
set Smallest=%d%
if %c% lss %Smallest% set Smallest=%c%
if %b% lss %Smallest% set Smallest=%b%
if %a% lss %Smallest% set Smallest=%a%
Echo Smallest number is %Smallest%
pause>nul
This is the way I would do it:
#echo off
setlocal
set /A c=%1, b=%2, a=%3
set /A "comp=((a-b)>>31)+1,one=!comp*a+comp*b,comp=((one-c)>>31)+1,Smallest=!comp*one+comp*c"
echo Smallest: %Smallest%
for /L %%i in (1,1,%Smallest%) do echo %%i
That is, if a > b then (a-b) is positive or zero (else, is negative), and (a-b)>>31 is zero (else, is -1 because >> is a signed shift of 31 bits) so finally ((a-b)>>31)+1 is one if a > b or is zero if a < b. In this way, we use this value comp to get b, or not this value !comp! to get a; and store the lesser of both in one variable.
The same method is used to get the lesser of one and c.
At end, we show all values from 1 to the lesser...
Here is an example code for this homework which outputs the smallest number and all numbers from 1 to the smallest number if the smallest number is not less than 1.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Smallest=%~1"
if not defined Smallest goto OutputHelp
if "%~2" == "" goto OutputHelp
for %%I in (%*) do if %%~I LSS %Smallest% set "Smallest=%%~I"
echo The smallest number is: %Smallest%
echo/
if %Smallest% GEQ 1 (
for /L %%I in (1,1,%Smallest%) do echo %%I
) else (
for /L %%I in (1,-1,%Smallest%) do echo %%I
)
goto EndBatch
:OutputHelp
echo %~nx0 must be run with at least two integer numbers as arguments.
echo/
echo Example: %~nx0 489 0x2AF 0715
echo/
echo An integer number beginning with 0x is interpreted hexadecimal.
echo An integer number beginning with 0 is interpreted as octal number
echo which cannot contain the digits 8 and 9 to be valid. An invalid
echo octal number is interpreted with value 0.
echo/
echo There is not checked if an argument string is a valid string for
echo a decimal, hexadecimal or octal integer number in the value
echo range -2147483648 to 2147483647 (32 bit signed integer).
:EndBatch
echo/
pause
endlocal
The output of this batch file on running it with the three numbers of the example without the output of all numbers from 1 to smallest number is:
The smallest number is: 0715
The octal number 0715 is decimal 461 and the hexadecimal number 0x2AF is decimal 687 which makes the octal number 0715 the smallest number.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
set /?
setlocal /?
I recommend to read also:
Safe number comparison in Windows batch file
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/

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

Converting time to seconds using arithmetic operators

I'm trying to convert a time value from a text file from hr:min:sec.sec to just seconds. I have a file with event numbers (consecutive order, 1,2,3 etc.) in column 1 of each row and the rest of the row is event data as in my example below. I want to have the user enter two numbers, with which my script grabs the corresponding hr:min:sec of each event and converts into only seconds.
The file format is 4 000-01:04:10.983745 34.56 string1 string_2 (this would be the 4th line, its date/time, a duration in seconds, and two static strings in the next two columns.
I am using a for loop to grab tokens 1, 2, and 3 using : as the delims and then just trimming the strings for the purpose of performing arithmetic.
So %%A should be 4 000-01, %%B should be 04, and %%C should be everything else on the line. Now I just read batch doesn't support decmials, so I can do without them if needed. But this isn't returning anything:
setlocal enabledelayedexpansion enableextension
REM auto-setting event values for testing
set begin=3
set end=4
for /f "tokens=1,2,3 delims=:" %%A in ('findstr /b /c:"!begin![^0-9]" event.txt') do (
set "hr=%%A"
set /A "min=%%B"
set "sec=%%C"
set /A "hr=!hr:~-2!"
set /A "sec=!sec:~0,2!"
set /A "total=(hr*3600)+(min*60)+sec"
echo !total!>>time.txt
)
exit /B
If your file format is:
line 4 000-01:04:10.983745 34.56 string1 string_2
delim - : : .
token 1 2 3 4
var - A B C
A common technic to avoid the leading zero/octal problem is to prefix
a two place decimal with a literal 1 and subtract 100.
Set /A allows multiple calculations on a line seperated by a comma, the vars don't need to be enclosed in percent signs (doesn't apply to for/arg vars).
#Echo off
setlocal enabledelayedexpansion enableextensions
for /f "tokens=2-4 delims=-:." %%A in (
'findstr /b /C:"4 " event.txt'
) do Set /A "hr=1%%A-100,min=1%%B-100,sec=1%%C-100,total=hr*3600+min*60+sec"
echo Total is %total% (hr=%hr%, min=%min%, sec=%sec%)
echo %total% >>time.txt
exit /B
Sample output:
Total is 3850 (hr=1, min=4,sec=10)
Here is an approach that regards the fractional seconds also, rounded to six fractional figures:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=event.txt"
set "_TARGET=time.txt"
set "_REGEX=^[0-9][0-9]* [0-3][0-9][0-9]-[0-9][0-9]:[0-5][0-9]:[0-5][0-9].[0-9][0-9]* "
rem // Convert filtered lines:
> "%_TARGET%" (
for /F "tokens=1-9* delims=:.- " %%A in ('findstr /R /C:"%_REGEX%" "%_SOURCE%"') do (
rem // Extract and store hour, minute, second values:
set "HOUR=1%%C" & set "MIN=1%%D" & set "SEC=1%%E"
rem // Extract and store fractional seconds and also the static strings:
set "FRAC=1%%F0000000" & set "STR1=%%I" & set "STR2=%%J"
rem // Convert hour, minute, second values to decimal integers:
set /A "HOUR%%=100, MIN%%=100, SEC%%=100"
setlocal EnableDelayedExpansion
rem // Convert fractional seconds to decimal number:
set /A "FRAC=!FRAC:~,7!%%1000000+!FRAC:~7,1!/5"
rem // Compute integer seconds, round to 6 decimal places:
set /A "SEC+=60*(MIN+60*HOUR)+FRAC/1000000" & set "FRAC=000000!FRAC!"
rem // Rebuilt line with time value replaced by fractional seconds:
echo %%A %%B-!SEC!.!FRAC:~-6! %%G.%%H !STR1! !STR2!
endlocal
)
)
endlocal
exit /B

Batch file multiply positive variables return a negative number

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.
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
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.
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..
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

how can i get disk space with decimal point in gb tb and mb [duplicate]

This question already has answers here:
Using wmic to get drives space information
(3 answers)
Closed 6 years ago.
I am trying to get the disk space in if it is MB GB or TB with 2 decimal part.
It should give the output with decimals
Here is my code:
WMIC LOGICALDISK GET Name,Size,FreeSpace | find /i "C:"
This is the slightly modified script from my answer to another question, which constitutes a pure batch-file solution: Get size of a directory in 'MB' using batch file.
Basically, all computations are done with numbers padded by two zeros to the right, which is the same as multiplying by a hundred, and the decimal separator is inserted next to the second digit from the right just for displaying. The drive to get the free space of needs to be given as a command line argument, like C: for example.
Here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define constants here:
set /A DIVISOR=1024 & rem (1024 Bytes = 1 KiB, 1024 KiB = 1 MiB,...)
set "ROUNDUP=" & rem (set to non-empty value to round up results)
set /A DECIMALS=2 & rem (define number of decimal places here)
set "PAD=" & for /L %%N in (1,1,%DECIMALS%) do call set "PAD=%%PAD%%0"
rem Get free space size of drive given as command line argument:
for /F "tokens=2 delims== " %%B in ('
wmic LogicalDisk WHERE 'DeviceID^="%~1"' GET FreeSpace /VALUE
') do for /F %%A in ("%%B") do set "BYTES=%%A"
if not defined BYTES set "BYTES=0"
rem Display result in Bytes and divide it to get KiB, MiB, etc.:
call :DIVIDE %BYTES%%PAD% 1 RESULT
call :DISPLAY %RESULT% %DECIMALS% STRING
echo( Bytes: %STRING%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
call :DISPLAY %RESULT% %DECIMALS% STRING
echo(KiBytes: %STRING%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
call :DISPLAY %RESULT% %DECIMALS% STRING
echo(MiBytes: %STRING%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
call :DISPLAY %RESULT% %DECIMALS% STRING
echo(GiBytes: %STRING%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
call :DISPLAY %RESULT% %DECIMALS% STRING
echo(TiBytes: %STRING%
endlocal
exit /B
:DIVIDE val_dividend val_divisor [ref_result] [ref_remainder]
rem Divide a huge number exceeding the 32-bit limitation
rem by a 32-bit number in the range from 1 to 1000000000;
rem the result might also exceed the 32-bit limitation.
setlocal EnableDelayedExpansion
set "DIVIDEND=%~1"
set "DIVISOR=%~2"
set "QUOTIENT=%~3"
set "REMAINDER=%~4"
rem Check whether dividend and divisor are given:
if not defined DIVIDEND (
>&2 echo(Too few arguments, dividend missing^^!
exit /B 2
) else if not defined DIVISOR (
>&2 echo(Too few arguments, divisor missing^^!
exit /B 2
)
rem Check whether dividend is purely numeric:
for /F "tokens=* delims=0123456789" %%N in ("!DIVIDEND!") do (
if not "%%N"=="" (
>&2 echo(Dividend must be numeric^^!
exit /B 2
)
)
rem Convert divisor to numeric value without leading zeros:
for /F "tokens=* delims=0" %%N in ("%DIVISOR%") do set "DIVISOR=%%N"
set /A DIVISOR+=0
rem Check divisor against its range:
if %DIVISOR% LEQ 0 (
>&2 echo(Divisor value must be positive^^!
exit /B 1
) else if %DIVISOR% GTR 1000000000 (
>&2 echo(Divisor value exceeds its limit^^!
exit /B 1
)
set "COLL=" & set "NEXT=" & set /A INDEX=0
rem Do a division digit by digit as one would do it on paper:
:LOOP
if "!DIVIDEND:~%INDEX%,1!"=="" goto :CONT
set "NEXT=!NEXT!!DIVIDEND:~%INDEX%,1!"
rem Remove trailing zeros as such denote octal numbers:
for /F "tokens=* delims=0" %%N in ("!NEXT!") do set "NEXT=%%N"
set /A NEXT+=0
set /A PART=NEXT/DIVISOR
set "COLL=!COLL!!PART!"
set /A NEXT-=PART*DIVISOR
set /A INDEX+=1
goto :LOOP
:CONT
rem Remove trailing zeros as such denote octal numbers:
for /F "tokens=* delims=0" %%N in ("%COLL%") do set "COLL=%%N"
if not defined COLL set "COLL=0"
rem Set return variables or display result if none are given:
if defined QUOTIENT (
if defined REMAINDER (
endlocal
set "%REMAINDER%=%NEXT%"
) else (
endlocal
)
set "%QUOTIENT%=%COLL%"
) else (
endlocal
echo(%COLL%
)
exit /B
:DISPLAY val_number val_decimals [ref_result]
rem Prepare fractional number for being displayed.
setlocal EnableDelayedExpansion
set "NUM=%~1"
set "DEC=%~2"
set "SEP=."
set "RET=%~3"
set "PAD=" & for /L %%N in (1,1,%DEC%) do set "PAD=!PAD!0"
set "NUM=%PAD%%NUM%"
set "NUM=!NUM:~,-%DEC%!%SEP%!NUM:~-%DEC%!"
for /F "tokens=* delims=0" %%N in ("%NUM%") do set "NUM=%%N"
if "%NUM:~,1%"=="%SEP%" set "NUM=0%NUM%"
if defined RET (
endlocal
set "%RET%=%NUM%"
) else (
endlocal
echo(%NUM%
)
endlocal
exit /B
Here is a sample output (supposing the script is saved as drive-size.bat):
C:\TEMP>drive-size.bat C:
Bytes: 82752724992.00
KiBytes: 80813208.00
MiBytes: 78919.14
GiBytes: 77.06
TiBytes: 0.07

Resources