简体   繁体   English

在Bash中使用bc舍入数字

[英]Rounding Numbers with bc in Bash

I want to compute an average with 3 decimal figures, rounded to nearest, using bc . 我想用bc计算一个带有3位小数的平均值,四舍五入到最接近的数字。

For example: 例如:

average of 3, 3 and 5 should yield 3.667 平均值为3,3和5应该产生3.667

and

average of 3, 3 and 4 should yield 3.333 平均值为3,3和4应该产生3.333

I tried: 我试过了:

echo "scale=3; $sum/$n+0.0005" | bc

but scale doesn't behave as I expect. scale并不像我预期的那样。 What can I do to solve my problem? 我该怎么做才能解决我的问题?

Your trick to add 0.0005 is not a bad idea. 你添加0.0005技巧并不是一个坏主意。 Though, it doesn't quite work that way. 虽然,它并不是那么有效。 scale is used internally when bc performs some operations (like divisions). bc执行某些操作(如分区)时,内部使用scale

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. 在你的情况下,最好先执行除法,可能使用scale-l开关到bc 1 (如果你的版本支持它),然后加0.0005然后设置scale=3并执行涉及scale的操作在内部进行截断。

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. 当然,无论sum是正数还是负数,您都希望以不同方式进行。 Fortunately, bc has some conditional operators. 幸运的是, bc有一些条件运算符。

`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 . 然后,您将要使用printf格式化此答案。

Wrapped in a function round (where you can optionally select the number of decimal figures): 包含在函数round (您可以选择小数位数):

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. 1使用-l开关, scale设置为20 ,这应该足够了。

Next function round argument 'x' to 'd' digits: 下一个函数将参数'x'转换为'd'数字:

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
} 

This solution is not that flexibile (it just converts float to int), but it can deal with negative numbers: 这个解决方案不是flexibile(它只是将float转换为int),但它可以处理负数:

e=$( echo "scale=0; (${e}+0.5)/1" | bc -l )
if [[ "${e}" -lt 0 ]] ; then
    e=$(( e - 1 ))
fi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM