1. Overview

Floating-point numbers are numbers made of an integer and a decimal component. Rounding floating-point numbers in the shell is a handy technique for various tasks. It’s useful for data manipulation and formatting output.

In this tutorial, we’ll explore different methods for rounding floating-point numbers. First, we’ll discuss several methods, including parameter expansion and external commands. After that, we’ll present a brief comparison of the methods.

2. Validating the Input

First, we may want to check that the input is a valid floating-point number.

To do that, we can use a regular expression:

$ [[ $number =~ ^[-+]?[0-9]+(\.[0-9]+)?$ ]]

Tools like awk or bc, which we’ll discuss later on, can extract the numeric value from user input or file contents.

Subsequently, we can use several built-in commands to round the floating-point number.

3. Using Parameter Expansion

Parameter expansion is a built-in functionality of the shell. We can use one of its features for basic rounding. However, with this method, there’s limited control over rounding behavior.

For instance, we can write a script that truncates the decimal part of a floating-point number:

$ cat shell_rounding.sh
#!/bin/bash

read -p "Enter a floating-point number: " number

rounded_number=${number%.*}

echo "Rounded number: $rounded_number"

Let’s try a quick example:

$ bash shell_rounding.sh 
Enter a floating-point number: 4.993
Rounded number: 4

This approach rounds down by default.

4. Using the printf Command

printf is part of the GNU Coreutils package. It’s more efficient and customizable than using basic parameter expansion for rounding.

Let’s create a rounding script:

$ cat round_to_integer.sh
#!/bin/bash

read -p "Enter a floating-point number: " number
rounded_number=$(printf "%.0f\n" $number)
echo "The rounded number: $rounded_number"

This method follows the rules of elementary arithmetic. Floating-point numbers with decimal values above 5 are rounded up, while decimal values below 5 are rounded down. For instance, 1.7 rounds up to 2, while 1.3 rounds down to 1.

Notably, when the decimal part is exactly 0.5, printf rounds to the nearest even integer. Let’s see some examples:

$ bash round_to_integer.sh
Enter a floating-point number: 1.5
The rounded number: 2
$ bash round_to_integer.sh    
Enter a floating-point number: 2.5
The rounded number: 2
$ bash round_to_integer.sh    
Enter a floating-point number: 3.5
The rounded number: 4

In this case, 2.5 rounds down to 2, which is the nearest even integer. This approach is called Banker’s Rounding. It is commonly used in financial calculations and by modern computers (IEEE 754 floating-point arithmetic).

5. Using a Custom Function

Another approach would be to create a custom function to handle rounding.

Let’s write a rounding function which we can later use:

$ cat round_function.sh
round()
{
  echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
}

The function takes two arguments: the number to round and the desired number of decimal places. It then uses printf %.$2f to format the output with the specified decimal places.

bc is a versatile calculator utility designed for arbitrary-precision arithmetic. It enables various mathematical operations, including rounding with high precision. However, it’s an external command and might require a separate installation to use.

Afterward, we can use the function in other scripts:

$ cat rounding.sh
#!/bin/bash

source round_function.sh 

read -p "Enter a floating-point number: " number
rounded_number=$(round $number 2)  

echo "The rounded number: $rounded_number"

First, we’re sourcing the round_function.sh script to make the round function available. After that, we can call the round function with the input number and the desired number of decimal places.

6. Using Awk

In the context of shell scripting, external commands refer to programs that aren’t built-in functionalities of the shell itself. They’re separate executable files located in system directories like /bin, /usr/bin, and so on.

We can use an external command like awk to round floating-point numbers. Awk is a scripting language specifically designed for processing text data line by line.

6.1. Rounding Down

Let’s suppose we have a text file with user input:

$ cat numbers.txt
3.14159
2.71828
10.5
-12.333

We can write an awk script to perform the rounding:

$ cat rounding_down.awk
{
    decimal = $1 - int($1)
    if (decimal < 0) {
        rounded = int($1) - 1
    } else {
        rounded = int($1)
    }
    print rounded
}

The curly braces define a code block that gets executed for each line of the input file. $1 refers to the first and only field in this case. int() is a built-in awk function. It converts the value to an integer by discarding the decimal part, thus rounding the floating-point number down to the nearest integer.

To perform the operation, we simply run the awk command along with the script and data file:

$ awk -f rounding_down.awk numbers.txt
3
2
10
-13

The result is always an integer, which is quite limited. What if we want to always round up?

6.2. Rounding Up

To round up, we’ll need to adjust the rounding.awk script to accommodate for that:

$ cat rounding_up.awk               
{
  decimal = $1 - int($1);
  if (decimal > 0) {
    rounded = int($1 + 1);
  } else {
    rounded = int($1);
  }
  print rounded
}

After that, we run the awk command:

$ awk -f rounding_up.awk numbers.txt
4
3
11
-12

Here, we’re checking the value of the decimal part. If it’s greater than 0, we add 1 and remove the decimal figures. This script effectively rounds up every floating-point number to the next integer.

6.3. Arithmetic Rounding

Lastly and most importantly, what if we want to stay in line with the rules of arithmetic? That is, we want to round up floating-point numbers with decimal parts greater than or equal to 0.5 and round down decimal parts less than 0.5.

Let’s adjust the script accordingly:

$ cat rounding.awk 
{
    decimal = $1 - int($1)
    if (decimal < 0) {
        rounded = int($1 - 0.5)
    } else {
        rounded = int($1 + 0.5)
    }
    print rounded
}

Then, we can run the awk command on the input file:

$ awk -f rounding.awk numbers.txt
3
3
11
-12

Now, we’re adding 0.5 to positive numbers. Therefore, if the decimal part was originally greater than or equal to 0.5, it rounds the number to the next integer. Conversely, if the decimal part was less than 0.5, the integer part remains unchanged.

For instance, adding 0.5 to 3.1 gives 3.6. It falls short of the next integer, and when we truncate the decimal part, the rounded number is 3. On the other hand, 2.7 plus 0.5 gives 3.2. When we truncate the decimal part, the number is rounded down to 3.

7. A Comparison of the Different Methods

Let’s compare the methods we’ve discussed:

Method Description Advantages Disadvantages Suitable for
Parameter Expansion Built-in functionality Simple, easy to use Limited control over rounding behavior Simple rounding tasks without high-precision requirements
Using the printf command Part of the GNU Coreutils package Efficient, customizable output formatting Limited functionality General purpose rounding tasks where arithmetic rounding behavior is desired
Custom Function User-defined function Very flexible and customizable Separate installation of bc adds complexity High-precision rounding tasks
Awk Scripting language for processing text data Processes multiple lines of data Rounds down by default. Requires additional script logic for other rounding behaviors General purpose rounding tasks with multiple lines of data

Choosing the right method depends largely on the task requirements and the desired output.

8. Conclusion

In summary, there are different methods for rounding floating-point numbers in the shell. Each method has a certain level of control over rounding behavior, output formatting, and precision.

In this article, we covered built-in functionalities like parameter expansion, along with printf and external commands like bc and awk. By understanding these techniques, we can effectively and precisely round floating-point numbers in shell scripts.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.