Reputation: 13
I would like to round a variable to the nearest 0.5 decimal in bash.
For instance, 1.3 should be 1.5, 1.15 should be 1, 2.8 should be 3, etc...
Upvotes: 0
Views: 1016
Reputation: 29250
Bash only (no call to bc
or other external calculator), with support for all kinds of decimal numbers with or without sign, with or without integer part and with or without fractional part:
function roundhalves () {
[[ $1 =~ ^([\+-]?)([0-9]*)\.([0-9]+)$ ]] || { printf "%s\n" "$1" && return; }
s=${BASH_REMATCH[1]}
a=${BASH_REMATCH[2]}
(( b = 1${BASH_REMATCH[3]} * 2 ))
[[ $b =~ ^([0-9][0-9])[0-9]*$ ]] && b=${BASH_REMATCH[1]}
(( b < 25 ? (b = 0) : b >= 35 ? (a += 1, b = 0) : (b = 5) ))
printf "%s%s.%d\n" "$s" "$a" "$b"
}
Note: the algorithm is simple: take the fractional part, prepend a 1, multiply by 2 and keep the two leading (leftmost) digits. The resulting integer is in the [20..39]
range. Compare with 25 and 35.
Note: the rounding is towards infinity but this is easy to modify.
Note: this is limited by the maximum representable numbers ([-2**63..2**63-1]
on 64 bits architectures). There is not such limitation with bc
. If your numbers may have a lot (more than 18) of digits either in the integer or fractional parts, and if you have it, prefer bc
.
Demo:
$ roundhalves 1.3
1.5
$ roundhalves 1.15
1.0
$ roundhalves 2.8
3
$ roundhalves 1.7499999
1.5
$ roundhalves 1.7500000
2.0
$ roundhalves -1.7
-1.5
Upvotes: 2
Reputation: 27
How about:
awk '{print int(($1+0.25)/0.5)*0.5}'.
PS. If you want to round a number to nearest x (such as 0.1, 50, 100, etc.), just use
awk '{print int(($1+x/2)/x)*x}'.
Upvotes: 1
Reputation: 319
This is an unoriginal answer fashioned after a very useful comment left by @CharlesDuffy on the accepted answer. The only difference is that I have removed the here strings so that I can use this function on both sh and bash.
roundhalves() {
printf '%s * 0.5\n' "$(printf '%1.f' "$(printf '%s * 2\n' "$1" | bc)")" | bc
}
Just thought it might be of use to anyone trying to write portable scripts.
Upvotes: 0
Reputation: 841
There's probably a simpler way to do this but this should work:
echo "1.3 * 2" | bc | xargs -I{ printf "%1.f" { | xargs -I% echo "% * .5" | bc
#1.5
echo "1.15 * 2" | bc | xargs -I{ printf "%1.f" { | xargs -I% echo "% * .5" | bc
#1.0
echo "2.8 * 2" | bc | xargs -I{ printf "%1.f" { | xargs -I% echo "% * .5" | bc
#3.0
So the only thing you're changing here is the first number of the echo value. If you want a function...
function roundhalves { echo "$1 * 2" | bc | xargs -I@ printf "%1.f" @ | xargs -I% echo "% * .5" | bc }
And just call it like this:
roundhalves 1.3
roundhalves 1.15
roundhalves 2.8
Upvotes: 1