Math in NT batch files

Introduction

Basic integer math functions using SET /A were first introduced in Windows NT 4.
With SET /A we can:

Add: SET /A Result = 12 + 4
Subtract: SET /A Result = 23 - 7
Multiply: SET /A Result = 8 * 2
Integer divide: SET /A Result = 33 / 2
Modulo divide: (1, 2) SET /A "Result = 66 %% 25"
Shift right: (2) SET /A "Result = 128 >> 3"
Shift left: SET /A "Result = 1 << 4"
Bitwise AND: SET /A "Result = 48 & 23"
Bitwise OR: SET /A "Result = 16 | 16"
Bitwise XOR: SET /A "Result = 31 ^ 15"
Group: SET /A "Result = ( 24 << 1 ) & 23"
 
We can combine operator and assignment:
 
SET Result=8
SET /A Result *= 2
 
which is a short notation for:
 
SET Result=8
SET /A Result = %Result% * 2
 
And we aren't limited to decimal numbers:
 
Octal: SET /A Result = 020
Decimal: SET /A Result = 16
Hexadecimal: SET /A Result = 0x10
Or any combination: SET /A Result = 010 + 0x20 - 24
 
The result is always returned as decimal, though.

By the way, in all the examples above, the value of environment variable Result will be 16.

Notes 1: Use double percent signs in batch files, or a single percent sign on the command line.
  2: Always use doublequotes if the expression contains one or more "special characters" (%, &, <, >, |, ˆ, ( or )) or if the variable name is omited (which displays the result directly on screen).

Missing functionality like exponentiation and (square) root can be emulated ("core" code marked red):

Notes 3: To download the code, right-click on the link and choose "Save target as" or "Save link as" or whatever comes closest.
  4: Hover your mouse pointer over the code to see it explained.

 

Limitations

There is a severe limitation in batch math: it can only handle 32-bit integers.

In Windows NT 4 and possibly 2000, the limitation is even worse: it can only handle unsigned 32-bit integers.

 

Workarounds: 32-bit

Workarounds for the 32-bit limitation include:

  1. dividing by 1000 (or any power of 10) by chopping off the last (3) digits
  2. splitting up the numbers into separate decimal digits and perform all the math and carry logic "manually"
  3. other scripting languages

Workaround #1 can be used to add up disk space, for example (relevant code marked red):

Notes 3: To download the code, right-click on the link and choose "Save target as" (or whatever comes closest).
  4: Hover your mouse pointer over the code to see it explained.

The trick is that each (big) number is treated as strings, then the rightmost 6 characters (digits) are chopped off, and only then the result is treated as a number.

This is a rather crude workaround, as it "rounds" all numbers before doing the math.
Adding half a MegaByte for each subdirectory (%Count% / 2) to %Total% does compensate for the truncations, though, so the grand total is more accurate than the individual numbers.
Note that the numbers don't represent "real" MegaBytes (1024 x 1024) buth rather Million Bytes (1000 x 1000).

Workaround #2 is perfectly demonstarted by Brian Williams' batch files:

Notes 3: To download the code, right-click on the link and choose "Save target as" (or whatever comes closest).

Perfect, but quite complex.

Workaround #3, other scripting languages, is self-explanatory.

I have often used temporary scripts, created "on-the-fly" by the batch file. Don't forget to clean up the "mess" afterwards.

The temporary script could be in any language supported on the computer. The most commonly supported scripting languages nowadays are VBScript and JScript. PowerShell comes close, but is not installed on every Windows computer yet.

Temporary VBScript or JSCript scripts need to be saved in temporary files before they can be "executed".

PowerShell scripts don't need to be saved to files first, as Kevin Ridenhour, L SFC RET, taught me:

Notes 3: To download the code, right-click on the link and choose "Save target as" (or whatever comes closest).
  4: Hover your mouse pointer over the code to see it explained.

The code has become rather complex, mainly because I ran into 2 problems using PowerShell like this:

  1. "{0:N2}" -f ( !DirSize! / 1048576 ) should divide DirSize by 1048576 (1MB) and display the result with 2 decimals; it does on the command line, but not in my batch files
  2. If the system uses a comma for the decimal delimiter (my Dutch system does), FOR loops will interpret it as an additional delimiter in the list (why can't humanity standardize something as simple as a decimal delimiter?)

Well, what I meant to demonstrate is that you can use PowerShell one-liners in batch files without the need for (temporary) script files.

But why not just create a PowerShell script instead?

Good question.
For this particular example, it would probably be a better choice indeed.

But there may be valid arguments to choose for batch files with embedded PowerShell commands:

An alternative to PowerShell are PHP based batch files, but they require PHP to be installed.
PHP is extremely good at math.

 

Workarounds: integers

There are no real workarounds that allow floating point math, except using other scripting languages.
The only exception may be if you have a limited and fixed number of decimals (e.g. 2), then you can just multiply everything by 100.
To display a decimal delimiter in the end results, concatenate the ineger divide by 100, followed by the decimal delimiter, followed by the modulo divide by 100:

SET Whole = Result / 100
SET "Fraction = Result %% 100"
SET Result=%Whole%.%Fraction%

This may break on the 32-bit limit, though.

In general, for floating point math I would recommend using other scripting languages.


page last uploaded: 4 December 2014, 11:25