I want to compute an average with 3 decimal figures, rounded to nearest, using bc
.
For example:
average of 3, 3 and 5 should yiel
Next function round argument 'x' to 'd' digits:
define r(x, d) {
auto r, s
if(0 > x) {
return -r(-x, d)
}
r = x + 0.5*10^-d
s = scale
scale = d
r = r*10/10
scale = s
return r
}
Your trick to add 0.0005
is not a bad idea. Though, it doesn't quite work that way. scale
is used internally when bc
performs some operations (like divisions).
In your case, it would be better to perform the division first, maybe using a large scale
or the -l
switch to bc
1 (if your version supports it), then add 0.0005
and then set scale=3
and perform an operation involving scale
internally to have the truncation performed.
Something like:
`a=$sum/$n+0.0005; scale=3; a/1`
Of course, you'll want to proceed differently whether sum
is positive or negative. Fortunately, bc
has some conditional operators.
`a=$sum/$n; if(a>0) a+=0.0005 else if (a<0) a-=0.0005; scale=3; a/1`
You'll then want to format this answer using printf
.
Wrapped in a function round
(where you can optionally select the number of decimal figures):
round() {
# $1 is expression to round (should be a valid bc expression)
# $2 is number of decimal figures (optional). Defaults to three if none given
local df=${2:-3}
printf '%.*f\n' "$df" "$(bc -l <<< "a=$1; if(a>0) a+=5/10^($df+1) else if (a<0) a-=5/10^($df+1); scale=$df; a/1")"
}
Try it:
gniourf$ round "(3+3+4)/3"
3.333
gniourf$ round "(3+3+5)/3"
3.667
gniourf$ round "-(3+3+5)/3"
-3.667
gniourf$ round 0
0.000
gniourf$ round 1/3 10
0.3333333333
gniourf$ round 0.0005
0.001
gniourf$ round 0.00049
0.000
1 with the -l
switch, scale
is set to 20
, which should be plenty enough.
This solution is not that flexibile (it just converts float to int), but it can deal with negative numbers:
e=$( echo "scale=0; (${e}+0.5)/1" | bc -l )
if [[ "${e}" -lt 0 ]] ; then
e=$(( e - 1 ))
fi