Skip to main content

batch-script-how-to-handle-floating-point-and-decimal-arithmetic

How to Handle Floating-Point and Decimal Arithmetic in Batch Script

The Windows Command Processor (cmd.exe) is a powerful tool for system automation, but it has a significant limitation: the SET /A command, its only built-in tool for math, only supports 32-bit signed integers. This means it cannot handle decimal numbers or fractions, a task often referred to as floating-point arithmetic.

However, with a clever workaround known as fixed-point arithmetic, you can perform calculations on decimal numbers with a predefined level of precision.

The Core Technique: Integers with Imaginary Decimals

The fundamental idea is to remove the decimal point before the calculation and add it back after. We do this by multiplying all our numbers by a "scaling factor" (a power of 10) to convert them into integers. After the calculation, we divide by the same factor to get the result.

The scaling factor you choose determines your precision.

  • For 2 decimal places, use a scaling factor of 100. (e.g., 12.34 becomes 1234)
  • For 3 decimal places, use a scaling factor of 1000. (e.g., 12.345 becomes 12345)

Simple Example: Addition

Let's say we want to calculate 12.055 + 1.001. We will use a precision of 3 decimal places, so our scaling factor is 1000.

  • Floating-Point: 12.055 + 1.001 = 13.056
  • Integer Conversion: 12055 + 1001 = 13056

The result 13056 represents 13.056.

@echo off
REM --- Simple addition with 3 decimal places of precision ---

REM We manually convert 12.055 to 12055 and 1.001 to 1001.
set /a result_int = 12055 + 1001

echo The integer result is: %result_int%
echo The conceptual floating-point result is: 13.056

Output:

The integer result is: 13056
The conceptual floating-point result is: 13.056

A Practical, Reusable Script

Manually converting numbers is impractical. The following script provides two helper functions, :floatToInt and :intToFloat, to automate the conversion process. It then demonstrates how to handle addition, subtraction, multiplication, and division correctly.

Key Concepts for Multiplication and Division:

  • Multiplication: When you multiply two scaled numbers (e.g., (a*1000) * (b*1000)), the result is scaled by the factor squared (result * 1000 * 1000). You must divide by the scaling factor to correct it.
  • Division: To avoid losing precision from integer division, you must pre-scale the numerator before dividing. Instead of (a*1000) / (b*1000), you calculate (a*1000*1000) / (b*1000).

Here is the complete, usable example:

@echo off
SETLOCAL

REM --- Configuration ---
REM Set our desired precision (number of decimal places)
SET "FP_PRECISION=3"
REM Calculate the scaling factor (10, 100, 1000, etc.)
SET /A "FP_SCALE=1"
FOR /L %%i IN (1,1,%FP_PRECISION%) DO SET /A "FP_SCALE*=10"

ECHO --- Batch Fixed-Point Floating Point Arithmetic ---
ECHO Precision: %FP_PRECISION% decimal places (Scaling Factor: %FP_SCALE%)
ECHO.

REM --- ADDITION EXAMPLE: 25.5 + 9.025 ---
CALL :floatToInt 25.5 num1_int
CALL :floatToInt 9.025 num2_int
SET /a "result_int = num1_int + num2_int"
CALL :intToFloat %result_int% result_float
ECHO 25.5 + 9.025 = %result_float%
ECHO.

REM --- SUBTRACTION EXAMPLE: 100 - 45.12 ---
CALL :floatToInt 100 num1_int
CALL :floatToInt 45.12 num2_int
SET /a "result_int = num1_int - num2_int"
CALL :intToFloat %result_int% result_float
ECHO 100 - 45.12 = %result_float%
ECHO.

REM --- MULTIPLICATION EXAMPLE: 12.5 * 2.5 ---
CALL :floatToInt 12.5 num1_int
CALL :floatToInt 2.5 num2_int
REM For multiplication, we must divide by the scale to correct the result
SET /a "result_int = (num1_int * num2_int) / FP_SCALE"
CALL :intToFloat %result_int% result_float
ECHO 12.5 * 2.5 = %result_float%
ECHO.

REM --- DIVISION EXAMPLE: 50.25 / 2 ---
CALL :floatToInt 50.25 num1_int
CALL :floatToInt 2 num2_int
REM For division, we must pre-scale the numerator to preserve precision
SET /a "result_int = (num1_int * FP_SCALE) / num2_int"
CALL :intToFloat %result_int% result_float
ECHO 50.25 / 2 = %result_float%
ECHO.

GOTO :EOF

REM ======================================================================
:floatToInt float_in [return_var]
:: Converts a floating point string (e.g., "123.45") to a scaled integer.
SETLOCAL
SET "float_str=%~1"
SET "integer_part=0"
SET "fraction_part=0"

FOR /F "tokens=1,2 delims=." %%A IN ("%float_str%") DO (
SET "integer_part=%%A"
IF "%%B" NEQ "" SET "fraction_part=%%B0000000000"
)

REM Pad fraction to the correct length and take the left-most part
SET "fraction_part=%fraction_part:~0,%FP_PRECISION%!"
SET /A "scaled_int = (integer_part * FP_SCALE) + fraction_part"

(
ENDLOCAL
IF "%~2" NEQ "" (SET %~2=%scaled_int%) ELSE (ECHO %scaled_int%)
)
EXIT /B


:intToFloat int_in [return_var]
:: Converts a scaled integer back to a floating point string.
SETLOCAL ENABLEDELAYEDEXPANSION
SET "scaled_int=%~1"
SET "is_negative="
IF %scaled_int% LSS 0 (
SET "is_negative=-"
SET /A "scaled_int = -scaled_int"
)

SET /A "integer_part = scaled_int / FP_SCALE"
SET /A "fraction_part = scaled_int %% FP_SCALE"

REM Pad fraction with leading zeros by adding the scale and taking the tail
SET /A "fraction_part = FP_SCALE + fraction_part"
SET "fraction_str=!fraction_part:~-%FP_PRECISION%!"

SET "float_str=%is_negative%%integer_part%.%fraction_str%"

(
ENDLOCAL
IF "%~2" NEQ "" (SET %~2=%float_str%) ELSE (ECHO %float_str%)
)
EXIT /B

Output:

--- Batch Fixed-Point Floating Point Arithmetic ---
Precision: 3 decimal places (Scaling Factor: 1000)

25.5 + 9.025 = 34.000

100 - 45.12 = 55.000

12.5 * 2.5 = 24.000

50.25 / 2 = 25.000

Limitations and Considerations

  1. Fixed Precision: You must decide on your required precision for all calculations beforehand. It's not truly "floating."
  2. Integer Overflow: SET /A uses 32-bit signed integers, with a maximum value of 2,147,483,647. If your number multiplied by the scaling factor exceeds this, your calculation will fail. This is especially risky during the intermediate steps of multiplication and division.
  3. Complexity: While powerful, this method adds complexity to your scripts. For heavy or complex numerical analysis, it is highly recommended to use a more capable scripting language like PowerShell or Python.

For simple tasks where a few decimal places are needed, this fixed-point technique is an excellent and effective solution to a common batch scripting limitation.