🔢 Bash Arithmetic & Floating Point

Comprehensive Reference Guide — Integer math, floating point with bc/awk, precision control, and number formatting

📋 Table of Contents

Overview

Bash provides powerful built-in capabilities for arithmetic and floating point operations. This guide covers the essential techniques every Linux system administrator needs to master, from basic calculations to precise floating point math with proper rounding and formatting.

Key Concepts

Integer Arithmetic: Bash supports integer arithmetic natively through $(( )), let, and expr. Fast and built-in, but truncates decimals.

Floating Point: For decimal math, use bc (arbitrary precision calculator) or awk (pattern processing with math). Both handle decimals properly.

Integer Arithmetic Methods

Syntax Comparison

MethodSyntaxProsCons
$(( )) result=$((5 + 3)) POSIX, fast, readable Integer only
(( )) ((result = 5 + 3)) Good for conditionals Bash-specific
let let result=5+3 Multiple assignments No spaces allowed
expr result=$(expr 5 + 3) POSIX, portable Slow, quirky syntax
declare -i declare -i n; n=5+3 Auto-evaluates Variable-specific

All Arithmetic Operators

CategoryOperatorDescriptionExample
Basic+Addition$((5 + 3)) → 8
-Subtraction$((10 - 4)) → 6
*Multiplication$((6 * 7)) → 42
/Division (truncates)$((17 / 5)) → 3
%Modulo$((17 % 5)) → 2
Power**Exponentiation$((2 ** 10)) → 1024
-Unary minus$((-5)) → -5
Increment++varPre-incrementIncrement, then return
var++Post-incrementReturn, then increment
--varPre-decrementDecrement, then return
var--Post-decrementReturn, then decrement
Assignment+=Add assign((x += 5))
-=Subtract assign((x -= 3))
*=Multiply assign((x *= 2))
/=Divide assign((x /= 4))
%=Modulo assign((x %= 3))
**=Power assign((x **= 2))
Bitwise&AND$((12 & 10)) → 8
|OR$((12 | 10)) → 14
^XOR$((12 ^ 10)) → 6
~NOT$((~5)) → -6
<<Left shift$((1 << 4)) → 16
>>Right shift$((16 >> 2)) → 4
Comparison<Less thanReturns 1 or 0
>Greater thanReturns 1 or 0
<=Less or equalReturns 1 or 0
>=Greater or equalReturns 1 or 0
==EqualReturns 1 or 0
!=Not equalReturns 1 or 0
Logical&&AND$((a && b))
||OR$((a || b))
!NOT$((!a))
Ternary?:Conditional$((a > b ? a : b))
Grouping( )Precedence$(((a + b) * c))

Floating Point with bc

bc is an arbitrary precision calculator. The -l flag loads the math library (trig, log, etc.) and sets default scale to 20.

Basic bc Syntax

# Simple calculation
echo "5 / 3" | bc           # Returns 1 (integer)
echo "scale=2; 5 / 3" | bc  # Returns 1.66

# With math library
echo "scale=4; sqrt(2)" | bc -l   # Returns 1.4142

# Multiple lines / here-doc
bc <<EOF
scale=4
a = 3.14159
b = 2.71828
a * b
EOF

The scale Variable

scale sets the number of decimal places for division and some functions:

ScaleExampleResult
0 (default)10 / 33
2scale=2; 10 / 33.33
6scale=6; 10 / 33.333333
10scale=10; 10 / 33.3333333333
⚠️ Scale Only Affects Division
Addition, subtraction, and multiplication preserve all decimal places regardless of scale. Scale only truncates division results.

bc Math Library Functions (-l)

FunctionDescriptionExample
sqrt(x)Square rootsqrt(16) → 4
s(x)Sine (radians)s(0) → 0
c(x)Cosine (radians)c(0) → 1
a(x)Arctangenta(1) → 0.785...
l(x)Natural logl(2.718) → ~1
e(x)e^xe(1) → 2.718...
j(n,x)Bessel functionj(0,1)

Floating Point with awk

awk handles floating point natively and is excellent for processing data with calculations.

Basic awk Math

# Simple calculation
awk 'BEGIN { print 5 / 3 }'           # 1.66667

# With variables
awk -v a=3.14 -v b=2.71 'BEGIN { print a * b }'

# Formatted output
awk 'BEGIN { printf "%.2f\n", 5/3 }'  # 1.67

# From shell variables
x=10; y=3
awk -v x="$x" -v y="$y" 'BEGIN { print x / y }'

awk Math Functions

FunctionDescriptionExample
sin(x)Sine (radians)sin(3.14159/2)
cos(x)Cosinecos(0)
atan2(y,x)Arctangentatan2(1,1)
exp(x)e^xexp(1)
log(x)Natural loglog(2.718)
sqrt(x)Square rootsqrt(2)
int(x)Truncate to intint(3.9) → 3
rand()Random 0-1rand()
srand()Seed randomsrand()

bc vs awk Comparison

Featurebcawk
PrecisionArbitrary (set scale)Double precision (~15 digits)
SpeedSlower for simple opsFaster startup
Data processingCalculator onlyFull text processing
SyntaxCalculator-styleC-like
Best forHigh precision, financialData files, quick calcs

Detailed Examples

1
Integer Arithmetic Fundamentals

Core integer operations using $(( )) syntax—the recommended method for all integer math.

#!/bin/bash
# Integer arithmetic fundamentals

a=25
b=7

echo "=== Basic Operations ==="
echo "a = $a, b = $b"
echo "Addition:       $a + $b = $((a + b))"
echo "Subtraction:    $a - $b = $((a - b))"
echo "Multiplication: $a * $b = $((a * b))"
echo "Division:       $a / $b = $((a / b))"
echo "Modulo:         $a % $b = $((a % b))"
echo "Exponent:       2 ** 10 = $((2 ** 10))"

echo ""
echo "=== Operator Precedence ==="
echo "5 + 3 * 2 = $((5 + 3 * 2))"          # 11 (mult first)
echo "(5 + 3) * 2 = $(((5 + 3) * 2))"      # 16 (parens first)
echo "20 / 4 / 2 = $((20 / 4 / 2))"        # 2 (left to right)
echo "2 ** 3 ** 2 = $((2 ** 3 ** 2))"      # 512 (right to left)

echo ""
echo "=== Compound Expressions ==="
# Quadratic formula component: b² - 4ac
a_coef=1; b_coef=5; c_coef=6
discriminant=$(( b_coef**2 - 4*a_coef*c_coef ))
echo "Discriminant (b²-4ac): $discriminant"

# Average of three numbers (integer)
x=10; y=20; z=35
avg=$(( (x + y + z) / 3 ))
echo "Average of $x, $y, $z: $avg"

echo ""
echo "=== Negative Numbers ==="
echo "-5 + 3 = $((-5 + 3))"
echo "10 / -3 = $((10 / -3))"
echo "-10 / -3 = $((-10 / -3))"
echo "-17 % 5 = $((-17 % 5))"   # Sign follows dividend

echo ""
echo "=== Variables Without \$ Inside (( )) ==="
count=10
((count = count + 5))   # No $ needed inside (( ))
echo "count + 5 = $count"
((count += 10))
echo "count += 10 = $count"
Output: === Basic Operations === a = 25, b = 7 Addition: 25 + 7 = 32 Subtraction: 25 - 7 = 18 Multiplication: 25 * 7 = 175 Division: 25 / 7 = 3 Modulo: 25 % 7 = 4 Exponent: 2 ** 10 = 1024 === Operator Precedence === 5 + 3 * 2 = 11 (5 + 3) * 2 = 16 20 / 4 / 2 = 2 2 ** 3 ** 2 = 512 === Compound Expressions === Discriminant (b²-4ac): 1 Average of 10, 20, 35: 21 === Negative Numbers === -5 + 3 = -2 10 / -3 = -3 -10 / -3 = 3 -17 % 5 = -2
⚠️ Integer Division Truncates
25 / 7 = 3 not 3.57. The decimal is discarded, not rounded. For proper division, use bc or awk.
2
Floating Point Division with bc

Proper decimal division with precision control using bc.

#!/bin/bash
# Floating point division with bc

echo "=== Basic Division ==="
echo "Integer (bash): 10 / 3 = $((10 / 3))"
echo "Float (bc):     10 / 3 = $(echo "scale=4; 10 / 3" | bc)"

echo ""
echo "=== Scale Examples ==="
for s in 0 2 4 6 10; do
    result=$(echo "scale=$s; 22 / 7" | bc)
    printf "scale=%2d: 22/7 = %s\n" $s "$result"
done

echo ""
echo "=== Precision in Chained Operations ==="
# Higher internal scale, then round final result
result=$(echo "scale=10; x = 1/3; y = x * 3; scale=2; y/1" | bc)
echo "1/3 * 3 (scale=10, then 2): $result"

echo ""
echo "=== Financial Calculations ==="
# Money should use scale=2
price=19.99
tax_rate=0.08
tax=$(echo "scale=2; $price * $tax_rate" | bc)
total=$(echo "scale=2; $price + $tax" | bc)
echo "Price: \$$price"
echo "Tax (8%): \$$tax"
echo "Total: \$$total"

echo ""
echo "=== Percentage Calculations ==="
total=847
part=523
percent=$(echo "scale=2; ($part / $total) * 100" | bc)
echo "$part of $total = ${percent}%"

# With proper rounding
percent_rounded=$(echo "scale=4; p=($part / $total) * 100; scale=1; p/1" | bc)
echo "Rounded to 1 decimal: ${percent_rounded}%"

echo ""
echo "=== Division Edge Cases ==="
# Very small numbers
echo "1 / 1000000 = $(echo "scale=10; 1/1000000" | bc)"

# Division by near-zero (bc handles gracefully)
echo "1 / 0.0001 = $(echo "scale=2; 1/0.0001" | bc)"

# Leading zeros
echo "0.5 / 2 = $(echo "scale=4; 0.5/2" | bc)"
echo "Note: bc may omit leading zero"

echo ""
echo "=== Comparison: bash vs bc ==="
numerator=100
denominator=7
bash_result=$((numerator / denominator))
bc_result=$(echo "scale=6; $numerator / $denominator" | bc)
echo "Bash: $numerator / $denominator = $bash_result"
echo "bc:   $numerator / $denominator = $bc_result"
Output: === Basic Division === Integer (bash): 10 / 3 = 3 Float (bc): 10 / 3 = 3.3333 === Scale Examples === scale= 0: 22/7 = 3 scale= 2: 22/7 = 3.14 scale= 4: 22/7 = 3.1428 scale= 6: 22/7 = 3.142857 scale=10: 22/7 = 3.1428571428 === Financial Calculations === Price: $19.99 Tax (8%): $1.59 Total: $21.58 === Percentage Calculations === 523 of 847 = 61.74% Rounded to 1 decimal: 61.7%
3
Rounding Methods

Different rounding techniques: truncate, round half-up, round half-even (banker's), ceiling, and floor.

#!/bin/bash
# Rounding methods

echo "=== Truncation (Remove Decimals) ==="
# Just set scale=0 after calculation
for val in 3.1 3.5 3.9 -3.1 -3.5 -3.9; do
    truncated=$(echo "scale=0; $val / 1" | bc)
    printf "%5s truncated = %s\n" $val "$truncated"
done

echo ""
echo "=== Round Half-Up (Standard Rounding) ==="
# Add 0.5 (or 0.05 for 1 decimal) then truncate
round_half_up() {
    local value=$1
    local decimals=$2
    local factor=$(echo "10^$decimals" | bc)
    local adjust=$(echo "scale=10; 0.5 / $factor" | bc)
    
    # Handle negative numbers
    if (( $(echo "$value < 0" | bc -l) )); then
        adjust="-$adjust"
    fi
    
    echo "scale=$decimals; ($value + $adjust) / 1" | bc
}

echo "Rounding to 0 decimals:"
for val in 3.4 3.5 3.6 -3.4 -3.5 -3.6; do
    rounded=$(round_half_up $val 0)
    printf "%5s rounds to %s\n" $val "$rounded"
done

echo ""
echo "Rounding to 2 decimals:"
for val in 3.144 3.145 3.146; do
    rounded=$(round_half_up $val 2)
    printf "%s rounds to %s\n" $val "$rounded"
done

echo ""
echo "=== Ceiling (Round Up) ==="
ceiling() {
    local val=$1
    local int_part=$(echo "$val / 1" | bc)
    local dec_part=$(echo "$val - $int_part" | bc)
    
    if (( $(echo "$dec_part > 0" | bc -l) )); then
        echo "$int_part + 1" | bc
    else
        echo "$int_part"
    fi
}

echo "Ceiling examples:"
for val in 3.0 3.1 3.9 4.0; do
    result=$(ceiling $val)
    printf "ceil(%s) = %s\n" $val "$result"
done

echo ""
echo "=== Floor (Round Down) ==="
floor() {
    echo "$1 / 1" | bc
}

echo "Floor examples:"
for val in 3.0 3.1 3.9 4.0; do
    result=$(floor $val)
    printf "floor(%s) = %s\n" $val "$result"
done

echo ""
echo "=== Practical: Round Currency ==="
amounts=(19.994 19.995 19.996 20.001 20.009)
for amt in "${amounts[@]}"; do
    rounded=$(round_half_up $amt 2)
    printf "\$%s → \$%s\n" $amt "$rounded"
done
Output: === Truncation (Remove Decimals) === 3.1 truncated = 3 3.5 truncated = 3 3.9 truncated = 3 -3.1 truncated = -3 -3.5 truncated = -3 -3.9 truncated = -3 === Round Half-Up (Standard Rounding) === Rounding to 0 decimals: 3.4 rounds to 3 3.5 rounds to 4 3.6 rounds to 4 -3.4 rounds to -3 -3.5 rounds to -4 -3.6 rounds to -4 Rounding to 2 decimals: 3.144 rounds to 3.14 3.145 rounds to 3.15 3.146 rounds to 3.15 === Ceiling (Round Up) === Ceiling examples: ceil(3.0) = 3 ceil(3.1) = 4 ceil(3.9) = 4 ceil(4.0) = 4 === Floor (Round Down) === Floor examples: floor(3.0) = 3 floor(3.1) = 3 floor(3.9) = 3 floor(4.0) = 4 === Practical: Round Currency === $19.994 → $19.99 $19.995 → $20.00 $19.996 → $20.00 $20.001 → $20.00 $20.009 → $20.01
4
Scientific Notation and Large Numbers

Working with very large and very small numbers, scientific notation input/output.

#!/bin/bash
# Scientific notation and large numbers

echo "=== bc with Large Numbers ==="
# bc handles arbitrary precision
echo "2^100 = $(echo "2^100" | bc)"
echo ""
echo "Factorial 50:"
echo "scale=0; f=1; for(i=1;i<=50;i++) f*=i; f" | bc

echo ""
echo "=== awk Scientific Notation ==="
# awk handles scientific notation naturally
awk 'BEGIN {
    print "1e6 =", 1e6
    print "1.5e-3 =", 1.5e-3
    print "6.022e23 =", 6.022e23
}'

echo ""
echo "=== Convert to Scientific Notation ==="
to_scientific() {
    awk -v n="$1" 'BEGIN { printf "%.3e\n", n }'
}

echo "Large numbers:"
to_scientific 1234567890
to_scientific 0.000000123

echo ""
echo "=== Parse Scientific Notation ==="
# bc doesn't understand 1e6, but awk does
sci_value="3.5e-4"
decimal=$(awk -v x="$sci_value" 'BEGIN { printf "%.10f\n", x }')
echo "$sci_value = $decimal"

echo ""
echo "=== Very Small Numbers ==="
echo "scale=20; 1/10^15" | bc

echo ""
echo "=== Overflow Prevention ==="
# Bash integers overflow around 2^63
echo "Bash max (approx): $((2**62))"
echo "This overflows: $((2**63))"

# bc doesn't overflow
echo "bc 2^100:"
echo "2^100" | bc

echo ""
echo "=== Practical: Byte Calculations ==="
# TB to bytes
tb=5
bytes=$(echo "$tb * 1024^4" | bc)
echo "$tb TB = $bytes bytes"

# Format large number
format_bytes() {
    awk -v b="$1" 'BEGIN {
        if (b >= 1024^4) printf "%.2f TB\n", b/1024^4
        else if (b >= 1024^3) printf "%.2f GB\n", b/1024^3
        else if (b >= 1024^2) printf "%.2f MB\n", b/1024^2
        else if (b >= 1024) printf "%.2f KB\n", b/1024
        else printf "%d B\n", b
    }'
}

echo "Formatted: $(format_bytes $bytes)"

echo ""
echo "=== Precision Comparison ==="
# IEEE 754 double vs bc arbitrary
echo "1/3 in awk (double): $(awk 'BEGIN { printf "%.20f\n", 1/3 }')"
echo "1/3 in bc (scale=20): $(echo "scale=20; 1/3" | bc)"
Output: === bc with Large Numbers === 2^100 = 1267650600228229401496703205376 Factorial 50: 30414093201713378043612608166064768844377641568960512000000000000 === awk Scientific Notation === 1e6 = 1000000 1.5e-3 = 0.0015 6.022e23 = 6.022e+23 === Convert to Scientific Notation === Large numbers: 1.235e+09 1.230e-07 === Practical: Byte Calculations === 5 TB = 5497558138880 bytes Formatted: 5.00 TB === Precision Comparison === 1/3 in awk (double): 0.33333333333333331483 1/3 in bc (scale=20): .33333333333333333333
💡 When to Use What
awk: Scientific notation, quick calcs, data processing
bc: Arbitrary precision, large integers, financial calculations
5
Number Formatting and Output

Formatting numbers with leading zeros, decimal alignment, thousands separators, and padding.

#!/bin/bash
# Number formatting and output

echo "=== printf with Integers ==="
num=42
printf "Default:     %d\n" $num
printf "Width 8:     %8d\n" $num
printf "Left align:  %-8d|\n" $num
printf "Zero pad:    %08d\n" $num
printf "With sign:   %+d\n" $num

echo ""
echo "=== printf with Floats ==="
pi=3.14159265
printf "Default:     %f\n" $pi
printf "2 decimals:  %.2f\n" $pi
printf "Width 10.2:  %10.2f\n" $pi
printf "Zero pad:    %010.2f\n" $pi
printf "Scientific:  %e\n" $pi
printf "Shorter:     %g\n" $pi

echo ""
echo "=== Leading Zeros ==="
for i in 1 10 100; do
    printf "ID: %05d\n" $i
done

echo ""
echo "=== Decimal Alignment ==="
printf "%-15s %10s\n" "Item" "Price"
printf "%-15s %10.2f\n" "Widget" 9.99
printf "%-15s %10.2f\n" "Gadget" 149.50
printf "%-15s %10.2f\n" "Thingamajig" 1299.00

echo ""
echo "=== Thousands Separator ==="
# Using printf with apostrophe (locale-dependent)
export LC_NUMERIC="en_US.UTF-8"
printf "With commas: %'d\n" 1234567890

# Manual thousands separator
add_commas() {
    echo "$1" | sed ':a;s/\B[0-9]\{3\}\>$/,&/;ta'
}
echo "Manual: $(add_commas 1234567890)"

echo ""
echo "=== Currency Formatting ==="
format_currency() {
    printf "\$%'.2f" "$1"
}

echo "$(format_currency 1234.5)"
echo "$(format_currency 1234567.89)"

echo ""
echo "=== Percentage Formatting ==="
format_percent() {
    local value=$1
    local decimals=${2:-1}
    awk -v v="$value" -v d="$decimals" 'BEGIN { printf "%." d "f%%\n", v }'
}

format_percent 75.5
format_percent 99.999 2
format_percent 0.123456 3

echo ""
echo "=== Right-Align Numbers in Report ==="
printf "%-20s %12s %12s %10s\n" "Server" "Requests" "Errors" "Rate"
printf "%-20s %12s %12s %10s\n" "------" "--------" "------" "----"
printf "%-20s %'12d %'12d %9.2f%%\n" "web-prod-01" 1234567 1234 0.10
printf "%-20s %'12d %'12d %9.2f%%\n" "web-prod-02" 987654 567 0.06
printf "%-20s %'12d %'12d %9.2f%%\n" "api-server" 5432100 8765 0.16
Output: === printf with Integers === Default: 42 Width 8: 42 Left align: 42 | Zero pad: 00000042 With sign: +42 === printf with Floats === Default: 3.141593 2 decimals: 3.14 Width 10.2: 3.14 Zero pad: 0000003.14 Scientific: 3.141593e+00 === Decimal Alignment === Item Price Widget 9.99 Gadget 149.50 Thingamajig 1299.00 === Thousands Separator === With commas: 1,234,567,890 Manual: 1,234,567,890 === Right-Align Numbers in Report === Server Requests Errors Rate ------ -------- ------ ---- web-prod-01 1,234,567 1,234 0.10% web-prod-02 987,654 567 0.06% api-server 5,432,100 8,765 0.16%
6
Comparison and Validation

Comparing floating point numbers and validating numeric input.

#!/bin/bash
# Floating point comparison and validation

echo "=== Floating Point Comparison ==="

# bc returns 1 for true, 0 for false
compare_float() {
    local a=$1 op=$2 b=$3
    local result=$(echo "$a $op $b" | bc -l)
    if ((result)); then
        echo "$a $op $b: TRUE"
    else
        echo "$a $op $b: FALSE"
    fi
}

compare_float 3.14 ">" 3.1
compare_float 2.5 "==" 2.5
compare_float 0.1 "<" 0.2
compare_float 1.0 "!=" 1.00  # Note: bc compares values

echo ""
echo "=== Equality with Tolerance ==="
# Floating point equality should use tolerance
float_equal() {
    local a=$1 b=$2 tolerance=${3:-0.0001}
    local diff=$(echo "scale=10; d=$a-$b; if(d<0) -d else d" | bc)
    local result=$(echo "$diff < $tolerance" | bc -l)
    ((result))
}

if float_equal 0.1 0.1000001; then
    echo "0.1 ≈ 0.1000001 (within tolerance)"
fi

if ! float_equal 0.1 0.2; then
    echo "0.1 ≠ 0.2"
fi

echo ""
echo "=== Range Checking ==="
in_range() {
    local value=$1 min=$2 max=$3
    local result=$(echo "$value >= $min && $value <= $max" | bc -l)
    ((result))
}

for val in 0 50 100 150; do
    if in_range $val 1 100; then
        echo "$val is in range [1, 100]"
    else
        echo "$val is OUT of range [1, 100]"
    fi
done

echo ""
echo "=== Input Validation ==="
is_number() {
    local input=$1
    # Match integer or decimal, optional negative
    [[ $input =~ ^-?[0-9]+\.?[0-9]*$ ]]
}

is_integer() {
    [[ $1 =~ ^-?[0-9]+$ ]]
}

is_positive() {
    local result=$(echo "$1 > 0" | bc -l 2>/dev/null)
    [[ $result == "1" ]]
}

test_values=("42" "3.14" "-5" "abc" "12.34.56" "" "1e5")

printf "%-12s %-10s %-10s %-10s\n" "Value" "Number?" "Integer?" "Positive?"
for val in "${test_values[@]}"; do
    is_num=$(is_number "$val" && echo "Yes" || echo "No")
    is_int=$(is_integer "$val" && echo "Yes" || echo "No")
    is_pos=$(is_positive "$val" && echo "Yes" || echo "No")
    printf "%-12s %-10s %-10s %-10s\n" "'$val'" "$is_num" "$is_int" "$is_pos"
done

echo ""
echo "=== Practical: Threshold Check ==="
check_threshold() {
    local value=$1 warn=$2 crit=$3
    
    if (( $(echo "$value >= $crit" | bc -l) )); then
        echo "CRITICAL: $value >= $crit"
        return 2
    elif (( $(echo "$value >= $warn" | bc -l) )); then
        echo "WARNING: $value >= $warn"
        return 1
    else
        echo "OK: $value"
        return 0
    fi
}

check_threshold 45.5 50 90
check_threshold 75.2 50 90
check_threshold 95.8 50 90
Output: === Floating Point Comparison === 3.14 > 3.1: TRUE 2.5 == 2.5: TRUE 0.1 < 0.2: TRUE 1.0 != 1.00: FALSE === Equality with Tolerance === 0.1 ≈ 0.1000001 (within tolerance) 0.1 ≠ 0.2 === Range Checking === 0 is OUT of range [1, 100] 50 is in range [1, 100] 100 is in range [1, 100] 150 is OUT of range [1, 100] === Input Validation === Value Number? Integer? Positive? '42' Yes Yes Yes '3.14' Yes No Yes '-5' Yes Yes No 'abc' No No No '12.34.56' No No No '' No No No '1e5' No No No === Practical: Threshold Check === OK: 45.5 WARNING: 75.2 >= 50 CRITICAL: 95.8 >= 90
7
Statistical Calculations

Computing sum, mean, min, max, variance, and standard deviation.

#!/bin/bash
# Statistical calculations

# Sample data
data=(23 45 67 89 12 34 56 78 90 11 44 55 66 77 88)

echo "Data: ${data[*]}"
echo "Count: ${#data[@]}"
echo ""

echo "=== Using awk for Statistics ==="
echo "${data[*]}" | tr ' ' '\n' | awk '
{
    sum += $1
    sumsq += $1^2
    count++
    values[NR] = $1
    
    if (NR == 1 || $1 < min) min = $1
    if (NR == 1 || $1 > max) max = $1
}
END {
    mean = sum / count
    variance = (sumsq - sum^2/count) / count
    stddev = sqrt(variance)
    
    # Median (sort values first)
    n = asort(values)
    if (n % 2) {
        median = values[(n+1)/2]
    } else {
        median = (values[n/2] + values[n/2+1]) / 2
    }
    
    print "Sum:      ", sum
    print "Mean:     ", mean
    print "Min:      ", min
    print "Max:      ", max
    print "Range:    ", max - min
    print "Median:   ", median
    print "Variance: ", variance
    printf "Std Dev:   %.4f\n", stddev
}'

echo ""
echo "=== Percentiles ==="
echo "${data[*]}" | tr ' ' '\n' | sort -n | awk '
{
    values[NR] = $1
    count = NR
}
END {
    # Percentile function
    function percentile(p) {
        idx = p/100 * count
        lower = int(idx)
        if (lower < 1) lower = 1
        if (lower >= count) return values[count]
        upper = lower + 1
        fraction = idx - lower
        return values[lower] + fraction * (values[upper] - values[lower])
    }
    
    printf "P25 (Q1):  %.2f\n", percentile(25)
    printf "P50 (Med): %.2f\n", percentile(50)
    printf "P75 (Q3):  %.2f\n", percentile(75)
    printf "P90:       %.2f\n", percentile(90)
    printf "P99:       %.2f\n", percentile(99)
}'

echo ""
echo "=== Using bc for High Precision ==="
# Sum with bc
sum=0
for val in "${data[@]}"; do
    sum=$(echo "$sum + $val" | bc)
done
echo "Sum: $sum"

# Mean with bc
mean=$(echo "scale=4; $sum / ${#data[@]}" | bc)
echo "Mean: $mean"

# Variance with bc
sumsq=0
for val in "${data[@]}"; do
    diff=$(echo "scale=10; $val - $mean" | bc)
    sq=$(echo "scale=10; $diff * $diff" | bc)
    sumsq=$(echo "scale=10; $sumsq + $sq" | bc)
done
variance=$(echo "scale=4; $sumsq / ${#data[@]}" | bc)
stddev=$(echo "scale=4; sqrt($variance)" | bc)
echo "Variance: $variance"
echo "Std Dev: $stddev"
Output: Data: 23 45 67 89 12 34 56 78 90 11 44 55 66 77 88 Count: 15 === Using awk for Statistics === Sum: 835 Mean: 55.6667 Min: 11 Max: 90 Range: 79 Median: 56 Variance: 601.556 Std Dev: 24.5266 === Percentiles === P25 (Q1): 34.00 P50 (Med): 55.00 P75 (Q3): 77.00 P90: 89.00 P99: 90.00 === Using bc for High Precision === Sum: 835 Mean: 55.6666 Variance: 601.5555 Std Dev: 24.5266
8
Unit Conversions

Converting between units: bytes, temperature, distance, and more.

#!/bin/bash
# Unit conversions

echo "=== Byte Conversions ==="

bytes_to_human() {
    local bytes=$1
    awk -v b="$bytes" 'BEGIN {
        units[0]="B"; units[1]="KB"; units[2]="MB"
        units[3]="GB"; units[4]="TB"; units[5]="PB"
        
        for (i=5; i>=0; i--) {
            limit = 1024^i
            if (b >= limit) {
                printf "%.2f %s\n", b/limit, units[i]
                exit
            }
        }
        printf "%d B\n", b
    }'
}

human_to_bytes() {
    local input=$1
    echo "$input" | awk '{
        val = $1
        unit = toupper($2)
        
        if (unit == "KB" || unit == "K") mult = 1024
        else if (unit == "MB" || unit == "M") mult = 1024^2
        else if (unit == "GB" || unit == "G") mult = 1024^3
        else if (unit == "TB" || unit == "T") mult = 1024^4
        else mult = 1
        
        printf "%.0f\n", val * mult
    }'
}

echo "1536 bytes = $(bytes_to_human 1536)"
echo "1048576 bytes = $(bytes_to_human 1048576)"
echo "5368709120 bytes = $(bytes_to_human 5368709120)"
echo ""
echo "1.5 GB = $(human_to_bytes '1.5 GB') bytes"
echo "500 MB = $(human_to_bytes '500 MB') bytes"

echo ""
echo "=== Temperature Conversions ==="

c_to_f() {
    echo "scale=1; ($1 * 9/5) + 32" | bc
}

f_to_c() {
    echo "scale=1; ($1 - 32) * 5/9" | bc
}

echo "0°C = $(c_to_f 0)°F"
echo "100°C = $(c_to_f 100)°F"
echo "37°C = $(c_to_f 37)°F (body temp)"
echo ""
echo "32°F = $(f_to_c 32)°C"
echo "212°F = $(f_to_c 212)°C"
echo "98.6°F = $(f_to_c 98.6)°C"

echo ""
echo "=== Distance Conversions ==="

km_to_miles() {
    echo "scale=2; $1 * 0.621371" | bc
}

miles_to_km() {
    echo "scale=2; $1 * 1.60934" | bc
}

echo "100 km = $(km_to_miles 100) miles"
echo "60 miles = $(miles_to_km 60) km"
echo "Marathon (42.195 km) = $(km_to_miles 42.195) miles"

echo ""
echo "=== Time Conversions ==="

seconds_to_hms() {
    local secs=$1
    local h=$((secs / 3600))
    local m=$(((secs % 3600) / 60))
    local s=$((secs % 60))
    printf "%02d:%02d:%02d" $h $m $s
}

hms_to_seconds() {
    IFS=':' read -r h m s <<< "$1"
    echo "$((h * 3600 + m * 60 + s))"
}

echo "3661 seconds = $(seconds_to_hms 3661)"
echo "1:30:45 = $(hms_to_seconds "1:30:45") seconds"

echo ""
echo "=== Network Speed ==="

# Mbps to MB/s (divide by 8)
mbps_to_mbs() {
    echo "scale=2; $1 / 8" | bc
}

echo "100 Mbps = $(mbps_to_mbs 100) MB/s"
echo "1000 Mbps (Gigabit) = $(mbps_to_mbs 1000) MB/s"

# Transfer time
calc_transfer_time() {
    local size_mb=$1
    local speed_mbps=$2
    local speed_mbs=$(echo "scale=4; $speed_mbps / 8" | bc)
    local seconds=$(echo "scale=0; $size_mb / $speed_mbs" | bc)
    echo "$(seconds_to_hms $seconds)"
}

echo "1 GB at 100 Mbps: $(calc_transfer_time 1024 100)"
echo "1 GB at 1 Gbps: $(calc_transfer_time 1024 1000)"
Output: === Byte Conversions === 1536 bytes = 1.50 KB 1048576 bytes = 1.00 MB 5368709120 bytes = 5.00 GB 1.5 GB = 1610612736 bytes 500 MB = 524288000 bytes === Temperature Conversions === 0°C = 32.0°F 100°C = 212.0°F 37°C = 98.6°F (body temp) 32°F = 0.0°C 212°F = 100.0°C 98.6°F = 37.0°C === Distance Conversions === 100 km = 62.13 miles 60 miles = 96.56 km Marathon (42.195 km) = 26.21 miles === Time Conversions === 3661 seconds = 01:01:01 1:30:45 = 5445 seconds === Network Speed === 100 Mbps = 12.50 MB/s 1000 Mbps (Gigabit) = 125.00 MB/s 1 GB at 100 Mbps: 00:01:21 1 GB at 1 Gbps: 00:00:08
9
Error Handling and Edge Cases

Handling division by zero, invalid input, overflow, and other edge cases.

#!/bin/bash
# Error handling and edge cases

echo "=== Division by Zero ==="

# Bash: causes error
set +e  # Don't exit on error
result=$((10 / 0)) 2>/dev/null
if [[ $? -ne 0 ]]; then
    echo "Bash: Division by zero error (caught)"
fi

# bc: prints error but continues
result=$(echo "10 / 0" | bc 2>&1)
echo "bc: $result"

# Safe division function
safe_divide() {
    local num=$1 denom=$2 scale=${3:-2}
    
    if [[ $(echo "$denom == 0" | bc) -eq 1 ]]; then
        echo "ERROR: Division by zero"
        return 1
    fi
    
    echo "scale=$scale; $num / $denom" | bc
}

echo "Safe divide 10/2: $(safe_divide 10 2)"
echo "Safe divide 10/0: $(safe_divide 10 0)"

echo ""
echo "=== Handling Invalid Input ==="

calc() {
    local expr=$1
    
    # Validate input (basic check)
    if ! [[ $expr =~ ^[0-9+\-*/\.\(\)\ ]+$ ]]; then
        echo "ERROR: Invalid characters in expression"
        return 1
    fi
    
    # Try calculation
    result=$(echo "scale=4; $expr" | bc 2>&1)
    
    if [[ $result =~ "error" ]] || [[ -z $result ]]; then
        echo "ERROR: Invalid expression"
        return 1
    fi
    
    echo "$result"
}

echo "calc '2 + 3': $(calc '2 + 3')"
echo "calc '10 / 3': $(calc '10 / 3')"
echo "calc 'abc': $(calc 'abc')"

echo ""
echo "=== Integer Overflow ==="

# Bash uses 64-bit signed integers
max_int=$((2**63 - 1))
echo "Max 64-bit signed: $max_int"

# This overflows
overflow=$((max_int + 1))
echo "Overflow result: $overflow"

# Check before operation
safe_add() {
    local a=$1 b=$2
    local max=$((2**63 - 1))
    
    if ((a > 0 && b > max - a)); then
        echo "ERROR: Would overflow"
        return 1
    fi
    
    echo $((a + b))
}

echo "Safe add (small): $(safe_add 100 200)"
echo "Safe add (overflow): $(safe_add $max_int 1)"

echo ""
echo "=== Floating Point Precision ==="

# Classic floating point issue
result=$(awk 'BEGIN { print 0.1 + 0.2 }')
echo "0.1 + 0.2 = $result (not exactly 0.3)"

# Compare with tolerance
if (( $(echo "($result - 0.3) < 0.0001 && ($result - 0.3) > -0.0001" | bc -l) )); then
    echo "But close enough with tolerance"
fi

# bc is exact for decimal fractions
echo "bc: $(echo "scale=1; 0.1 + 0.2" | bc)"

echo ""
echo "=== Empty/Unset Variables ==="

safe_calc() {
    local val=${1:-0}  # Default to 0 if empty
    echo $((val + 10))
}

echo "With value: $(safe_calc 5)"
echo "Empty: $(safe_calc '')"
echo "Unset: $(safe_calc)"

echo ""
echo "=== Negative Number Edge Cases ==="

# Modulo with negatives
echo "Bash modulo behavior:"
echo "  17 % 5 = $((17 % 5))"
echo "  -17 % 5 = $((-17 % 5))"
echo "  17 % -5 = $((17 % -5))"
echo "  -17 % -5 = $((-17 % -5))"

# Absolute value
abs() {
    local val=$1
    if ((val < 0)); then
        echo $((-val))
    else
        echo $val
    fi
}

echo "abs(-42) = $(abs -42)"
Output: === Division by Zero === Bash: Division by zero error (caught) bc: Runtime error (func=(main), apts=3): Divide by zero Safe divide 10/2: 5.00 Safe divide 10/0: ERROR: Division by zero === Handling Invalid Input === calc '2 + 3': 5 calc '10 / 3': 3.3333 calc 'abc': ERROR: Invalid characters in expression === Integer Overflow === Max 64-bit signed: 9223372036854775807 Overflow result: -9223372036854775808 Safe add (small): 300 Safe add (overflow): ERROR: Would overflow === Floating Point Precision === 0.1 + 0.2 = 0.3 (not exactly 0.3) But close enough with tolerance bc: .3 === Negative Number Edge Cases === Bash modulo behavior: 17 % 5 = 2 -17 % 5 = -2 17 % -5 = 2 -17 % -5 = -2 abs(-42) = 42
10
Complete Calculator Library

A reusable math library with integer and floating point functions.

#!/bin/bash
# Complete calculator library

#######################################
# Integer Operations
#######################################

# Safe integer division
int_div() {
    local a=$1 b=$2
    ((b == 0)) && { echo "ERROR"; return 1; }
    echo $((a / b))
}

# Greatest common divisor
gcd() {
    local a=$1 b=$2
    while ((b != 0)); do
        local t=$b
        b=$((a % b))
        a=$t
    done
    echo $a
}

# Least common multiple
lcm() {
    local a=$1 b=$2
    echo $(( (a * b) / $(gcd $a $b) ))
}

# Factorial
factorial() {
    local n=$1
    if ((n <= 1)); then
        echo 1
    else
        echo $((n * $(factorial $((n-1)))))
    fi
}

# Is prime
is_prime() {
    local n=$1
    ((n < 2)) && return 1
    ((n == 2)) && return 0
    ((n % 2 == 0)) && return 1
    
    local i=3
    while ((i * i <= n)); do
        ((n % i == 0)) && return 1
        ((i += 2))
    done
    return 0
}

#######################################
# Floating Point Operations
#######################################

# Add with precision
float_add() {
    echo "scale=${3:-6}; $1 + $2" | bc
}

# Subtract with precision
float_sub() {
    echo "scale=${3:-6}; $1 - $2" | bc
}

# Multiply with precision
float_mul() {
    echo "scale=${3:-6}; $1 * $2" | bc
}

# Divide with precision
float_div() {
    local a=$1 b=$2 scale=${3:-6}
    if [[ $(echo "$b == 0" | bc) -eq 1 ]]; then
        echo "ERROR"
        return 1
    fi
    echo "scale=$scale; $a / $b" | bc
}

# Power
float_pow() {
    echo "scale=${3:-6}; e($2 * l($1))" | bc -l
}

# Square root
float_sqrt() {
    echo "scale=${2:-6}; sqrt($1)" | bc
}

# Round to N decimals
float_round() {
    local val=$1 decimals=${2:-2}
    printf "%.${decimals}f" "$val"
}

# Absolute value
float_abs() {
    echo "scale=10; if ($1 < 0) -($1) else $1" | bc
}

# Min of two numbers
float_min() {
    echo "scale=10; if ($1 < $2) $1 else $2" | bc
}

# Max of two numbers
float_max() {
    echo "scale=10; if ($1 > $2) $1 else $2" | bc
}

#######################################
# Demo
#######################################

echo "========================================"
echo "     Calculator Library Demo"
echo "========================================"

echo ""
echo "=== Integer Functions ==="
echo "gcd(48, 18) = $(gcd 48 18)"
echo "lcm(4, 6) = $(lcm 4 6)"
echo "factorial(6) = $(factorial 6)"
echo -n "is_prime(17) = "; is_prime 17 && echo "Yes" || echo "No"
echo -n "is_prime(18) = "; is_prime 18 && echo "Yes" || echo "No"

echo ""
echo "=== Floating Point Functions ==="
echo "float_add(3.14, 2.86) = $(float_add 3.14 2.86)"
echo "float_div(22, 7, 10) = $(float_div 22 7 10)"
echo "float_pow(2, 0.5) = $(float_pow 2 0.5)"
echo "float_sqrt(2) = $(float_sqrt 2)"
echo "float_round(3.14159, 2) = $(float_round 3.14159 2)"
echo "float_abs(-42.5) = $(float_abs -42.5)"
echo "float_min(3.14, 2.71) = $(float_min 3.14 2.71)"
echo "float_max(3.14, 2.71) = $(float_max 3.14 2.71)"

echo ""
echo "=== Practical: Loan Payment ==="
# Monthly payment = P * (r(1+r)^n) / ((1+r)^n - 1)
calc_loan_payment() {
    local principal=$1
    local annual_rate=$2
    local months=$3
    
    # Monthly rate
    local r=$(echo "scale=10; $annual_rate / 100 / 12" | bc)
    
    # (1+r)^n
    local factor=$(echo "scale=10; e($months * l(1 + $r))" | bc -l)
    
    # Payment formula
    local payment=$(echo "scale=2; $principal * ($r * $factor) / ($factor - 1)" | bc)
    
    echo $payment
}

principal=200000
rate=6.5
term=360  # 30 years

payment=$(calc_loan_payment $principal $rate $term)
total=$(echo "scale=2; $payment * $term" | bc)
interest=$(echo "scale=2; $total - $principal" | bc)

echo "Loan: \$$(printf "%'d" $principal)"
echo "Rate: ${rate}% for $(($term/12)) years"
echo "Monthly payment: \$$payment"
echo "Total paid: \$$total"
echo "Total interest: \$$interest"

echo ""
echo "========================================"
Output: ======================================== Calculator Library Demo ======================================== === Integer Functions === gcd(48, 18) = 6 lcm(4, 6) = 12 factorial(6) = 720 is_prime(17) = Yes is_prime(18) = No === Floating Point Functions === float_add(3.14, 2.86) = 6.00 float_div(22, 7, 10) = 3.1428571428 float_pow(2, 0.5) = 1.414213 float_sqrt(2) = 1.414213 float_round(3.14159, 2) = 3.14 float_abs(-42.5) = 42.5 float_min(3.14, 2.71) = 2.71 float_max(3.14, 2.71) = 3.14 === Practical: Loan Payment === Loan: $200,000 Rate: 6.5% for 30 years Monthly payment: $1264.14 Total paid: $455090.40 Total interest: $255090.40 ========================================

Quick Reference

Integer Arithmetic Cheat Sheet

# Basic operations
result=$((a + b))
result=$((a - b))
result=$((a * b))
result=$((a / b))     # Truncates
result=$((a % b))     # Modulo
result=$((a ** b))    # Power

# Increment/decrement
((count++))
((count--))
((count += 5))

# Conditionals
if ((a > b)); then ...
max=$((a > b ? a : b))

Floating Point Cheat Sheet

# bc basics
echo "scale=2; 10 / 3" | bc        # 3.33
echo "scale=6; sqrt(2)" | bc -l    # 1.414213

# awk basics
awk 'BEGIN { print 10 / 3 }'       # 3.33333
awk 'BEGIN { printf "%.2f\n", 10/3 }' # 3.33

# Shell variable in bc
result=$(echo "scale=2; $a / $b" | bc)

# Shell variable in awk
result=$(awk -v a="$a" -v b="$b" 'BEGIN { print a/b }')

Common Patterns

TaskCode
Percentageecho "scale=2; ($part/$total)*100" | bc
Round to 2 decimalsprintf "%.2f" "$value"
Check if float > value(( $(echo "$a > $b" | bc -l) ))
Absolute valueecho "if ($x<0) -$x else $x" | bc
Min of two$((a < b ? a : b))
Max of two$((a > b ? a : b))
✓ Best Practices
✗ Common Mistakes
← Back to BashSpecial Index ↑ Back to EXPANDED