21

Ok, so I'm trying to round up an input of 17.92857, so that it gets an input of 17.929 in bash.

My code so far is:

read input
echo "scale = 3; $input" | bc -l

However, when I use this, it doesn't round up, it returns 17.928.

Does anyone know any solutions to this?

Quill
  • 2,729
  • 1
  • 33
  • 44
  • [look at this](http://stackoverflow.com/a/2395601/3913686) –  Oct 20 '14 at 12:18
  • You can use `printf -v output "%.3f\n" "$input"` to assign printf's output to variable $output. – Cyrus Oct 20 '14 at 12:46
  • 1
    Must be coming from hackerrank (https://www.hackerrank.com/challenges/bash-tutorials---arithmetic-operations) – bergie3000 Feb 03 '17 at 18:12

5 Answers5

34

In case input contains a number, there is no need for an external command like bc. You can just use printf:

printf "%.3f\n" "$input"

Edit: In case the input is a formula, you should however use bc as in one of the following commands:

printf "%.3f\n" $(bc -l <<< "$input")
printf "%.3f\n" $(echo "$input" | bc -l)
Tim Zimmermann
  • 6,132
  • 3
  • 30
  • 36
7

To extend Tim's answer, you can write a shell helper function round ${FLOAT} ${PRECISION} for this:

#!/usr/bin/env bash

round() {
  printf "%.${2}f" "${1}"
}

PI=3.14159

round ${PI} 0
echo
round ${PI} 1
echo
round ${PI} 2
echo
round ${PI} 3
echo
round ${PI} 4
echo
round ${PI} 5
echo
round ${PI} 6
echo

# Outputs:
3
3.1
3.14
3.142
3.1416
3.14159
3.141590

# To store in a variable:
ROUND_PI=$(round ${PI} 3)
echo ${ROUND_PI}

# Outputs:
3.142
Zane
  • 4,652
  • 1
  • 29
  • 26
1

Even already answered by Tim in 2014 I want to share an improved version of Zane's function

#!/usr/bin/env bash

# round a float number to the given decimal digits
# if $2 is not given or not a positive integer its set to zero

# float must be in international format, this is more save for scripts
# to use other format replace LC_ALL=C with your language.

_round_float() {
     local digit="${2}"; [[ "${2}" =~ ^[0-9]+$ ]] || digit="0"
     LC_ALL=C printf "%.${digit}f" "${1}"
}

some examples how to use the function:

# round pi constant
PI=3.141599236

_round_float $PI 0   3
_round_float $PI 1   3.1
_round_float $PI 3   3.142
_round_float $PI 9   3.141599236
_round_float $PI -3  3
_round_float $PI abc 3

you can also adjust the comma position aka divide/multiply by 10 on the fly

 #!/bin/bash
 # change metric base unit
 UNIT=1234.567890

 # comma 1 position right, multiply by 10
 _round_float ${UNIT}e1 3     12345.678

 # comma 3 positions left, eg convert milli seconds to seconds
 _round_float ${UNIT}e-3 3     1.234

 # comma 3 positions right, eg convert m3 to liters
 _round_float ${UNIT}e3 0     1234567
Gnadelwartz
  • 1,542
  • 1
  • 10
  • 14
0

A little trick is to add 0.0005 to your input, this way you will have your number round up correctly.

Ludovic Feltz
  • 11,416
  • 4
  • 47
  • 63
  • Or like @Tim said use printf – Ludovic Feltz Oct 20 '14 at 12:21
  • If you're rounding to three places, you should actually add `.0005`, not `.005`. But why can't you do that? – Mark Reed Oct 20 '14 at 12:35
  • But you have to set a larger scale > 3 first (else 0.0005 = 0.000), and then set scale = 3 and compute the result using, e.g., `x / 1`. There may also be unexpected results for negative x and you may be required to use `if ( x < 0 ) { x = x - 0.0005; } else { x = x + 0.0005 }` or similar. – Max Mar 16 '19 at 19:19
-3

if you're receiving the round-off error with the number 17.928 try this: read y v=echo "scale = 3; $y" |bc -l if [ $v == 17.928 ] ; then echo "17.929" else echo $v fi

reyb
  • 1