When working with numbers, we sometimes want to display them in an alternative format, e.g. scientific notation, hexadecimal, or GB with a single decimal.
Though NT batch files are capable of handling octal and hexadecimal numbers besides decimal, numbers are always displayed in plain decimal.
For alternative display formats, we will have to write our own batch code.
Displaying numbers in hexadecimal is not as hard as it may seem: integer divide the number by 16, and convert the remainder to a single hexadecimal digit; repeat until there is nothing left to integer divide.
In practical code:
:: Delayed variable expansion is required SETLOCAL ENABLEDELAYEDEXPANSION :: Variable Decimal holds the decimal number SET Decimal=987654321 :: Variable Convert is a helper variable SET Convert=0123456789ABCDEF :: Variable Hexadecimal will hold the end result SET Hexadecimal= :: Variable Scratch holds the intermediate results SET Scratch=%Decimal% :Loop :: Variable LSD holds the intermediate value's Least Significant Digit SET /A "LSD = %Scratch% %% 16" :: Convert LSD to hexadecimal (this is where delayed variable expansion is required) SET LSD=!Convert:~%LSD%,1! :: Prepend LSD to the hexadecimal string SET Hexadecimal=%LSD%%Hexadecimal% :: Integer divide by 16 by shifting 4 bits (1 hexadecimal digit) "over the edge" SET /A "Scratch = %Scratch% >> 4" :: Alternative: SET /A "Scratch = %Scratch% / 16" :: Calculate the next digit if the intermediate value Scratch is still greater than 0 IF NOT %Scratch% EQU 0 GOTO Loop :: Add 0x prefix to the hexadecimal string SET Hexadecimal=0x%Hexadecimal% :: Show decimal input and hexadecimal output SET Decimal SET Hexadecimal ENDLOCAL
You can test the result using one of the following commands:
SET /A %Hexadecimal%
(before ENDLOCAL
), which should show the initial decimal value again, orIF %Decimal% EQU %Hexadecimal% ECHO OK
, which should just show the message OK
If you don't mind using PowerShell, displaying numbers in hexadecimal is a lot easier:
SET Decimal=987654321 FOR /F %%A IN ('powershell -C "\""0x{0:X8}\"" -f %Decimal%"') DO SET Hexadecimal=%%A :: Or: FOR /F %%A IN ('powershell -C "[Convert]::ToString( %Decimal%, 16 )"') DO SET Hexadecimal=0x%%A :: Show decimal input and hexadecimal output SET Decimal SET Hexadecimal
Displaying numbers in octal is even easier than hexadecimal, as there is no need for delayed variable expansion.
In practical code:
:: Variable Decimal holds the decimal number SET Decimal=987654321 :: Variable Octal will hold the end result SET Octal= :: Variable Scratch holds the intermediate results SET Scratch=%Decimal% :Loop :: Variable LSD holds the intermediate value's Least Significant Digit SET /A "LSD = %Scratch% %% 8" :: Prepend LSD to the Octal string SET Octal=%LSD%%Octal% :: Integer divide by 8 by shifting 3 bits (1 octal digit) "over the edge" SET /A "Scratch = %Scratch% >> 3" :: Alternative: SET /A "Scratch = %Scratch% / 8" :: Calculate the next digit if the intermediate value Scratch is still greater than 0 IF NOT %Scratch% EQU 0 GOTO Loop :: Add leading 0 to the octal string SET Octal=0%Octal% :: Show decimal input and octal output SET Decimal SET Octal
Testing the result of the octal "conversion" is similar to testing the result of the hexadecimal "conversion":
SET /A %Octal%
should show the initial decimal value again, orIF %Decimal% EQU %Octal% ECHO OK
should just show the message OK
If you don't mind using PowerShell, displaying numbers in octal is a lot easier:
SET Decimal=987654321 FOR /F %%A IN ('powershell -C "[Convert]::ToString( %Decimal%, 8 )"') DO SET Octal=%%A :: Show decimal input and octal output SET Decimal SET Octal
A list of files with their size in MBs with a single decimal digit calls for right alignment.
An example:
@ECHO OFF SETLOCAL ENABLEDELAYEDEXPANSION :: Calculate 1 MB SET /A MB = 1024 * 1024 FOR %%A IN (*.mp3) DO ( SET /A "FileSize = ( ( 20 * %%~zA / %MB% ) + 1 ) / 2" SET /A "Fraction = !FileSize! %% 10" SET /A "Whole = !FileSize! / 10" SET Align= !Whole!.!Fraction! SET Align=!Align:~-8! ECHO !Align! MB %%~nA ) ENDLOCAL
The line SET /A "FileSize = ( ( 20 * %%~zA / %MB% ) + 1 ) / 2"
takes the file size (%%~zA
, multiplies it by 20, divides it by 1 MB, adds 1, and then divides it all by 2.
Why?
Without a decimal, it would have been like this: SET /A "FileSize = ( ( 2 * %%~zA / %MB% ) + 1 ) / 2"
.
What we really do here, is overcome some of the limitations of integer math.
Multiplying by 2, adding 1, and then integer dividing by 2 is the integer-only equivalent to taking the integer value of a ( floating number + 0.5 ), which is the proper way to correctly round floating numbers to integers.
By multiplying by 20 instead of 2, we get 10 times the result, allowing us to extract the whole and a 1-digit fraction in the lines following this line.
Variable Align prefixes the result with 8 spaces (SET Align= !Whole!.!Fraction!
), end is then chopped to 8 characters from the right (SET Align=!Align:~-8!
).
Note: | By multiplying the decimal number (file size) by 20, we limit the code to files of 107,374,182 bytes and less (0x7FFFFFFF / 20), or 102 MB. For larger files, negative numbers will be displayed. By not dividing by 1 MB but 0.1 MB, and then multiplying by 2 instead of 20, we may correctly handle files of about 1020 MB, at the cost of a slight accuracy decrease. |
The result will look like this:
9.2 MB Al Stewart - Year of the Cat 6.4 MB Bolland en Bolland - You're in the army now 8.5 MB David Bowie - Heroes 7.0 MB David Bowie - Space Oddity 22.5 MB Earth and Fire - Atlantis 24.7 MB Earth and Fire - Song of the Marching Children 4.1 MB Elvis Presley - Can't Help Falling In Love 5.1 MB Enya - Orinoco Flow 6.8 MB Europe - The Final Countdown 6.1 MB Heart - Barracuda 6.5 MB Jon and Vangelis - I'll Find My Way Home 6.9 MB Jon and Vangelis - So Long Ago So Clear 11.0 MB Led Zeppelin - Stairway To Heaven 3.1 MB Louis Armstrong - What a Wonderful World 5.5 MB Procol Harum - A Whiter Shade of Pale 4.3 MB The Beatles - Girl 4.4 MB The Beatles - Hey Bulldog 4.8 MB The Beatles - Lucy In The Sky With Diamonds 6.1 MB The Moody Blues - Nights in White Satin 8.3 MB Vangelis - La petite fille de la mer 12.4 MB Vangelis - To the Unknown Man
See the Batch Files Math page for more details on handling floating numbers.
In this case it may be a good idea to drop batch and use "pure" PowerShell instead:
Get-ItemProperty -Path '*.mp3' | ForEach-Object { "{0,8:F1} MB {1}" -f ( $_.Length / 1MB ), [System.IO.Path]::GetFileNameWithoutExtension( $_.Name ) }
Believe me, there comes a point where you no longer want to wrap these long PowerShell commands in batch code, and you're getting very close...
I cannot get the PowerShell "one-liner" shown above to work when wrapped in batch, but I can mix batch and PowerShell code and make that work:
FOR %%A IN (*.mp3) DO @powershell -C "\""{0,8:F1} MB`t{1}\"" -f ( %~zA / 1MB ), \""%~nA\"""
You see, batch is easier than PowerShell for getting file name and size.
Using PowerShell to format the number solves the file size limit too.
Note that when wrapping a "template string" with multiple spaces in batch, those multiple spaces will be replaced by a single space, hence the tab (`t
) between MB
and {1}
.
Check out ToString.exe, a batch tool to format numbers and dates the .NET way.
With this tool, the MP3 list above can be created using the following command:
FOR %%A IN (*.mp3) DO @FOR /F %%B IN ('SET /A %%~zA / 1048576') DO @ToString.exe "{0,8:F1} MB\t{1}" %%B "%%~nA"
or for better readability:
@ECHO OFF FOR %A IN (*.mp3) DO ( FOR /F %B IN ('SET /A %~zA / 1048576') DO ( ToString.exe "{0,8:F1} MB\t{1}" %B "%~nA" ) )
Besides simpler code compared to "pure batch", the file sizes will be more accurate and not limited to 102 MB, and the decimal separator will depend on your computer's culture.
Or how about this basic hexadecimal table:
FOR /L %%A IN (1,1,32) DO @ToString.exe "{0,2}\t0x{0:X2}" %%A
Or show today's date in ISO formats:
ToString.exe "{0:yyyy-MM-dd}" %Date%
or:
ToString.exe "{0:yyyyMMddTHHmmssfffzzz}" %Time%
page last modified: 2022-03-01; loaded in 0.0014 seconds