# Performing Math Calculation in Bash

Using Math in Bash scripts becomes a necessary evil when writing complex crontab reports, monitoring plugins, setup scripts with dynamic configurations, or any other kind of automation like showing a Raspbery PI CPU temperature. There is always some type of calculations to be made.

This post cover a few examples on how to do basic mathematical operations (elementary arithmetic like multiplication and addition) in Bash with * integers* or

*numbers.*

**floating-points**## Introduction: Integer and Floating Points

Before we get into the details on how to do Math in Bash, remember that an * integer* is a

*whole number*that is not a fraction and is anywhere from zero to positive or negative infinity. For example 42, 36, and -12 are integers, while 3.14 and √2 are not. The set of integers include all negative whole numbers, zero, and all positive whole numbers. An integer and its opposite are the same distance from zero.

A floating-point number is a computing programming term to represent a real number with fractional part. For example 3.14, √2, -10.5, 4^{e-2} are floating points numbers. A floating-point is specified by a base (binary or decimal), a precision, and an exponent range. It is usually in binary made of 32 (simple precision) or 64 bits (double precision), thought the IEEE 754 standard mention more formats.

Format | Total bits | Significand bits | Exponent bits | Smallest number | Largest number |
---|---|---|---|---|---|

Single precision | 32 | 23 + 1 sign | 8 | ≈ 1.2 ⋅ 10-38 | ≈ 3.4 ⋅ 1038 |

Double precision | 64 | 52 + 1 sign | 11 | ≈ 2.2 ⋅ 10-308 | ≈ 1.8 ⋅ 10308 |

You may see a floating-point being represented in the form of **significand x base**^{exponent}. For example: *3.14 = 314 x 10 ^{-2}*

## Doing Math in Bash with Integer

### Using *expr* command line

The legacy way to do math calculations with * integer*, and only

*integer*, has been for a long time to use the

`expr`

(*evaluate expression*) command line. Though, this method can be slow as it is not a shell builtin and will fork new process, hence not ideal in a large for-loop. Also, the

`expr`

behavior may vary between systems depending on the implementation of the tool.```
# Substraction
[me@host ~]$ expr 1 + 1
0
# Addition
[me@host ~]$ expr 1 + 1
2
# Assign result to a variable
[me@host ~]$ myvar=$(expr 1 + 1)
[me@host ~]$ echo $myvar
2
# Addition with a variable
[me@host ~]$ expr $myvar + 1
3
# Division
[me@host ~]$ expr $myvar / 3
0
# Multiplication
[me@host ~]$ expr $myvar \* 3
6
```

⚠️ When doing a

multiply bymake sure to backslash (`\`

) the asterisk (`*`

) as it is also awildcardused for expansion in Bash.

### Using *let* builtin command

An alternative to `expr`

command, is to use the Bash builtin command `let`

which as a large list of supported operators.

id++, id-- | variable post-increment, post-decrement |

++id, --id | variable pre-increment, pre-decrement |

-, + | unary minus, plus |

!, ~ | logical and bitwise negation |

** | exponentiation |

*, /, % | multiplication, division, remainder |

+, - | addition, subtraction |

<<, >> | left and right bitwise shifts |

<=, >=, <, > | comparison |

==, != | equality, inequality |

& | bitwise AND |

^ | bitwise XOR |

| | bitwise OR |

&& | logical AND |

|| | logical OR |

expr ? expr : expr | conditional operator |

=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= | assignment |

```
[me@host ~]$ myvar=6 && echo $myvar
6
[me@host ~]$ let myvar+=1
[me@host ~]$ echo $myvar
7
[me@host ~]$ let myvar+1
[me@host ~]$ echo $myvar
8
[me@host ~]$ let myvar2=myvar+1
[me@host ~]$ echo $myvar2
9
```

You can combine expressions on a single line as `let`

evaluate each argument as an arithmetic expression.

```
[me@host ~]$ let x=4 y=5 z=x*y u=z/2
[me@host ~]$ echo $x $y $z $u
4 5 20 10
```

### Using *Arithmetic Expansion* with $((...)) or $[...]

The recommended way to do operations with integer in Bash is to use the **Arithmetic Expansion** capability of the shell. The builtin shell expansion allow you to use the parentheses `$((...))`

or square brackets `$[...]`

to do math. The shell expansion will return the result of the latest expression given. Note that the square brackets notation is being deprecated and should be avoided.

```
[me@host ~]$ myvar=3 && echo $myvar
3
[me@host ~]$ echo $((myvar+2))
5
[me@host ~]$ echo $[myvar+2]
5
[me@host ~]$ myvar=$((myvar+3))
[me@host ~]$ echo $myvar
6
```

This allow you to use *C-style* programming and easily increment or decrement a variable in Bash using the `++`

or `--`

operators. All the operators listed in the `let`

table above are fully available when using **Arithmetic Expansion**.

```
[me@host ~]$ myvar=3 && echo $myvar
3
[me@host ~]$ echo $((myvar++))
3
[me@host ~]$ echo $myvar
4
[me@host ~]$ echo $((++myvar))
5
[me@host ~]$ echo $myvar
5
```

In the previous section, we show an example with `let`

containing multiple expressions on a single line. This is also possible with *Arithmetic Expansion*. The only difference is that multiple expressions must be seprated by a comma (`,`

).

```
[me@host ~]$ echo $((x=4,y=5,z=x*y,u=z/2))
10
[me@host ~]$ echo $x $y $z $u
4 5 20 10
```

### Using the *compound command* ((...)) or the *declare* builtin

In many cases you may not need to use the shell **Arithmetic Expansion** as you don't need the resulted value right away. In such cases you may prefere to use the * compound command* in bash which are simply the parentheses

`((...))`

. It is the same as the **Arithmetic Expansion**without the dollar sign (

`$`

). Alternatively, you can also use the `declare`

builtin command with the `-i`

option.For example, the dollar sign (`$`

) when incrementing a counter in a `for loop`

or executing an expression. For example, you can simply use `((++myvar))`

or `((myvar=3*a))`

.

```
[me@host ~]$ ((x=4,y=5,z=x*y,u=z/2))
[me@host ~]$ echo $x $y $z $u
4 5 20 10
[me@host ~]$ declare -i x=4 y=5 z=x*y u=z/2
[me@host ~]$ echo $x $y $z $u
4 5 20 10
```

### Arithmetic Expansion: *syntax error: invalid arithmetic operator*

When using *Arithmetic Expansion*, you may encounter the following error:

```
syntax error: invalid arithmetic operator (error token is ...)
```

This error is generally due to improperly formated variables or integer used in the expression. The most common mistake would be to try to use a floating-point which would fail as such.

```
[me@host ~]$ echo "$((20.0+7))"
-bash: 20.0/7: syntax error: invalid arithmetic operator (error token is ".0+7")
```

Another error would be when assigning a value to a variable with *hidden* characters in it and then use that variable in the arithmetic expansion.

```
[me@host ~]$ a=$' 3\r'
[me@host ~]$ echo "$((a+7))"
")syntax error: invalid arithmetic operator (error token is "
```

You will notice that in such case the error message even get mangled due to the `\r`

in the variable. To prevent such issue, you will want to ensure the variable you use is properly formated by stripping out control characters which are mostly the character with ascii value from 1 (octal 001) to 31 (octal 037). You can do that by using the shell parameter expansion.

```
[me@host ~]$ echo "$((${a//[ $'\001'-$'\037']}+7))"
10
```

👉 Prior to Bash version 5, you may need to ensure the right collating order of the ascii values by using

`shopt -s globasciiranges`

. Though, since Bash version 5, you don't need to worry about the`globasciiranges`

option which is now set by default. Read more about Bash 5 with the post What's New in GNU Bash 5?

### Example: Using *Arithmetic Expansion* and *printf* to do math on a date

Below is a simple example at doing a date manipulation with a math substraction in shell by using the new `$EPOCHSECONDS`

variable from GNU Bash version 5 and `printf`

date formating. The example shows the current time minus 86400 seconds.

```
$ echo $EPOCHSECONDS
1589153003
$ printf '%(%d)T\n' $EPOCHSECONDS
10
$ printf '%(%d)T\n' $((EPOCHSECONDS-86400))
9
```

👉 To read more on how to manipulate and format dates in

see the post How To Format Date and Time in Linux, macOS, and Bash?.bash

## Doing Floating-point Arithmetic in Bash

### Using *printf* builtin command

A noteworthy but unconventional way to do floating-point arithmetic in native bash is to combine *Arithmetic Expansion* with *printf* using a *scientifc notation*. Since, you can't do floating-point in bash, you would just apply a given multiplier by a power of 10 to your math operation inside an Arithmetic Expansion, then use printf to display the float.

For example, the operation `2/3`

in Artithmetic Expansion as `$((2/3))`

would return `0`

. In order to get the floating number using `printf`

you would use a formula like below where `<precision>`

would be the floating-oint precision to display and `<multiplier>`

the power of ten muliplier. A multiplier of 3 would mean `10**3`

which is `1000`

.

```
printf %.<precision>f "$((10**<multiplier> * 2/3))e-<multiplier>
```

Note that the floating point precision in `%.<precision>f`

shouldn't be higher than the multiplier itself as it will just fill with zeros.

```
[me@host ~]$ printf %.3f "$((10**3 * 2/3))e-3"
0.666
[me@host ~]$ printf %.1f "$((10**3 * 2/3))e-3"
0.7
[me@host ~]$ printf %.5f "$((10**3 * 2/3))e-3"
0.66600
```

### Using *GNU awk* command line

Another way to do floating-point arithmetic is to use *GNU awk*:

```
[me@host ~]$ awk "BEGIN {print 100/3}"
33.3333
```

You can use the `printf`

function to adjust the precision of the results:

```
[me@host ~]$ awk "BEGIN {printf \"%.2f\n\", 100/3}"
33.33
```

When using negative values, make sure to leave white space between signs.

```
[me@host ~]$ awk "BEGIN {print -8.4--8}"
awk: cmd. line:1: BEGIN {print -8.4--8}
awk: cmd. line:1: ^ syntax error
```

```
[me@host ~]$ awk "BEGIN {print -8.4 - -8}"
-0.4
```

### Using *GNU bc* command line

You can't do floating-point arithmetic natively in Bash, you will have to use a command line tool, the most common one being "*bc - An arbitrary precision calculator language*". To start the interactive mode, you simply need to type `bc`

in your command prompt.

```
[me@host ~]$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
3*5.2+7/8
15.6
15.6+299.33*2.3/7.4
108.6
```

Of course you can also use the `STDIN`

to send your formula to `bc`

then get the output on `STDOUT`

.

```
[me@host ~]$ echo "3.4+7/8-(5.94*3.14)" | bc
-15.25
```

or by using the here-doc notation:

```
[me@host ~]$ bc <<< "3.4+7/8-(5.94*3.14)"
-15.25
```

I encourage you too take a look at the man pages to get more detail on how it works (*man bc*).

There are four special variables,scale,ibase,obase, andlast.scaledefines how some operations use digits after the decimal point. The default value ofscaleis 0.ibaseandobasedefine the conversion base for input and output numbers. The default for both input and output is base 10.last(an extension) is a variable that has the value of the last printed number.

The "*scale*" variable is really important for the precision of your results, especially when using integers only.

👉

you can also useNote:`bc -l`

to usemathliband see the result at max scale.

```
[me@host ~]$ echo "2/3" | bc
0
[me@host ~]$ echo "scale=2; 2/3" | bc
.66
[me@host ~]$ echo "(2/3)+(7/8)" | bc
0
[me@host ~]$ echo "scale=2;(2/3)+(7/8)" | bc
1.53
[me@host ~]$ echo "scale=4;(2/3)+(7/8)" | bc
1.5416
[me@host ~]$ echo "scale=6;(2/3)+(7/8)" | bc
1.541666
[me@host ~]$ echo "(2/3)+(7/8)" | bc -l
1.54166666666666666666
```

👉 If you want to go further, check my post Advanced Math Calculation in Bash using GNU bc.