Bash Arithmetic: Integer and Floating-Point Math

By 

Published on

8 min read

Integer and floating-point arithmetic in Bash

Bash treats everything as a string by default, which catches many people out the first time they write total=$count+1 and end up with the literal text 5+1 instead of 6. The shell has dedicated arithmetic forms that evaluate numeric expressions properly, but they only handle integers; for floating-point math you reach for bc or awk. Once you know which form to use where, the math is short and predictable.

This guide explains how Bash arithmetic works, the differences between (( )), $(( )), and let, and how to combine them with external tools when you need decimals.

Why Plain Assignment Does Not Add

A first attempt at incrementing a variable usually looks like this:

sh
count=5
total=$count+1
echo "$total"
output
5+1

The shell sees $count as the string 5, joins it with +1, and assigns the result. To force numeric evaluation, use one of the arithmetic forms below.

The (( )) Compound Command

Wrap an expression in double parentheses to evaluate it as integer math:

sh
count=5
(( count = count + 1 ))
echo "$count"
output
6

Inside (( )), variable names work without $, the C-style operators are available (+, -, *, /, %, **, ++, --, <<, >>, &, |, ^, &&, ||), and the exit status of the whole expression is non-zero when the result is 0, which means you can use arithmetic in if:

sh
if (( count > 5 )); then
    echo "count is above 5"
fi
output
count is above 5

The shortcut increment and decrement operators read cleanly in loops:

sh
for (( i = 0; i < 3; i++ )); do
    echo "i=$i"
done
output
i=0
i=1
i=2

(( )) is the right form when you assign back to a variable or use the result for control flow. It does not print anything to standard output.

The $(( )) Expansion

When you want the value of an expression rather than the side effect, use the $(( )) arithmetic expansion. The result is substituted into the surrounding command like any other expansion:

sh
count=5
echo "Next: $(( count + 1 ))"
output
Next: 6

$(( )) is the right form for inline math inside echo, assignments to other variables, command arguments, and printf:

sh
files=42
echo "Average: $(( files / 7 )) per day"
output
Average: 6 per day

Notice that the division truncates: 42 / 7 happens to be exact, but 43 / 7 would yield 6, not 6.14. Bash arithmetic is integer-only; the decimal part is silently discarded.

The let Builtin

let is the older builtin for arithmetic. It evaluates each argument as an expression:

sh
let "count = 5 + 1"
echo "$count"
output
6

let is mostly historical at this point. (( )) does the same work without the quoting trap (let count=* would expand the * as a glob unless quoted) and reads more cleanly. New scripts should prefer (( )); let is worth recognizing in code you inherit.

Operators You Will Actually Use

Bash supports a wide list of arithmetic operators. These are the ones that come up in everyday scripts:

  • +, -, *, /, % - Standard arithmetic and remainder.
  • ** - Exponentiation.
  • ++, -- - Increment and decrement, prefix or postfix.
  • +=, -=, *=, /=, %= - Compound assignment.
  • ==, !=, <, <=, >, >= - Numeric comparison (inside (( ))).
  • &&, ||, ! - Logical AND, OR, NOT.
  • <<, >>, &, |, ^, ~ - Bitwise shifts and operations.

A small example that uses several:

sh
size=1024
if (( size > 0 && size % 2 == 0 )); then
    echo "Power of two? $(( (size & (size - 1)) == 0 ? 1 : 0 ))"
fi
output
Power of two? 1

The ternary operator (? :) works inside arithmetic contexts, which keeps short branches readable without nesting if.

Number Bases

Bash arithmetic supports several integer bases. Prefix a number to declare its base:

  • 0xNN for hexadecimal.
  • 0NN for octal.
  • BASE#NNN for any base from 2 to 64.

Examples:

sh
echo "$(( 0xff ))"
echo "$(( 0755 ))"
echo "$(( 2#1010 ))"
output
255
493
10

Watch out for leading zeros: 08 is interpreted as octal and rejected because 8 is not a valid octal digit. If user input may include leading zeros (HTTP status codes parsed from a string, timestamps), strip them before arithmetic with ${var#0} or use the explicit 10# base prefix:

sh
value="08"
echo "$(( 10#$value ))"
output
8

Floating-Point Math with bc

Bash itself cannot do decimal math, but it is happy to call out to a tool that can. bc is the arbitrary-precision calculator that ships with most distributions:

sh
echo "scale=2; 43 / 7" | bc
output
6.14

The scale=2 directive sets two decimal places of precision. Without it, bc truncates the division the same way Bash does. Use a higher scale for currency or scientific work:

sh
echo "scale=10; 4*a(1)" | bc -l
output
3.1415926532

The -l flag loads the math library, which gives you s (sine), c (cosine), a (arctangent), l (natural log), e (exponential), and sqrt. The expression above computes pi from 4 * arctan(1).

To capture the result into a variable:

sh
pi=$(echo "scale=4; 4*a(1)" | bc -l)
echo "pi = $pi"
output
pi = 3.1415

bc is the right tool when you need genuine decimals and do not want a heavyweight dependency. For one-off interactive calculations, run bc -l and type expressions at its prompt.

Floating-Point Math with awk

awk is the other obvious choice and is often faster because it does not start a separate calculator process. Use awk for inline expressions inside scripts:

sh
rate=$(awk 'BEGIN { printf "%.2f\n", 43 / 7 }')
echo "rate = $rate"
output
rate = 6.14

printf inside awk works the same way as the C function, with %.2f controlling the number of decimal places. For a percentage:

sh
pct=$(awk -v a=23 -v b=89 'BEGIN { printf "%.1f%%\n", (a / b) * 100 }')
echo "pct = $pct"
output
pct = 25.8%

Pass values into awk with -v name=value rather than building the expression by string concatenation; the -v form avoids quoting headaches and shell-injection issues when the inputs come from user data.

Increment Counters in a Loop

Counter loops are the bread and butter of arithmetic in scripts, and they lean on the increment and decrement operators . Two equivalent forms work; pick the one that reads best:

sh
count=0
while IFS= read -r line; do
    (( count++ ))
done < input.txt
echo "Lines: $count"
sh
count=0
while IFS= read -r line; do
    count=$(( count + 1 ))
done < input.txt
echo "Lines: $count"

The (( count++ )) form is shorter and is what most authors prefer. Both produce the same result and run at the same speed.

If the script uses set -e, avoid (( count++ )) when count may start at zero. The arithmetic command returns a failure status when the expression evaluates to 0, so the first increment can stop the script. Use pre-increment or assignment instead:

sh
(( ++count ))
(( count += 1 ))

Troubleshooting

syntax error in expression
A variable that should hold a number contains non-numeric characters (often a leading or trailing space from read, or a stray letter). Inside (( )) and $(( )), Bash reports a syntax error on the offending token. Trim the input with ${var//[[:space:]]/} or validate it before the arithmetic line.

division by 0
A divisor evaluated to zero, often because the variable was empty and Bash treated it as 0. Quote the expansion in your check (if [ -n "$divisor" ]) before the divide, and either skip the operation or substitute a default with ${divisor:-1}.

Result is 0 when it should be a fraction
Bash arithmetic is integer-only. Switch to bc with an explicit scale= or to awk with %.Nf formatting for decimal output.

FAQ

When should I use (( )) versus $(( ))?
Use (( )) for assignments and control flow (if, while, for). Use $(( )) when you want the value substituted into a surrounding command. They share the same expression syntax.

Can I do floating-point math without an external tool?
Not in Bash. ksh93 and zsh support floating-point in their arithmetic contexts; Bash does not. For Bash, route decimals through bc or awk.

Is there a performance difference between bc and awk?
awk is usually faster because it runs entirely inside a single process and does not communicate over a pipe. For one-off calculations the difference is negligible; in a tight loop, prefer awk or precompute the values with a single awk call instead of running bc per iteration.

Conclusion

Reach for (( )) and $(( )) whenever a script needs counters, conditions, or quick integer math, and switch to bc or awk the moment decimals enter the picture. Pair that habit with Bash strict mode and the patterns in our Bash best practices guide so numeric code keeps working even when the inputs do not.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page