我有 3 个变量和值:
totalLines=14
outsideLines=6
multiplied=600
totalLines
表示总行数(100%),而outsideLines
表示时间戳值超出一定限制的行数。我的目的是计算这些外线的百分比。如果我这样做:
percentage=$(( multiplied / totalLines))
和 echo $percentage
,我得到结果:
42
但是,我想生成浮点数的百分比,如下所示:
42.85
我尝试实现这一点:
percentage=$( bc <<< 'scale=2; $multiplied / $totalLines' )
失败并出现以下错误:
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: L
(standard_in) 1: syntax error
我应该如何正确使用 bc 以获得浮点数的百分比?
答案1
简单的
如果您需要做的只是(近似值,四舍五入到零)数学,您可以直接在大多数 shell 中使用以下命令进行计算:
➤ printf '%8.2f\n' "$((multiplied*100/totalLines))e-2"
42.85
并且,在 bash 或 zsh 或 ksh93 的最新版本中,您可以分配给这样的变量:
➤ printf -v percentage '%.2f\n' "$((multiplied*100/totalLines))e-2"
➤ echo "$percentage"
42.85
这是一个百分比,四舍五入到零,采用整数运算,保留两位小数,没有空格。
这是因为 shell 执行整数数学运算,但我们始终可以计算该值乘以 100。这将使$((…))
整数的结果为 4285。
然后所需要做的就是将小数点分隔符(点)向左移动两位。
正因如此e-2
,价值才会如此4285e-2
。
然后,将结果格式化为保留两位小数的浮点数%8.2f
。
一个更便携的选项,但速度稍慢(子 shell 意味着 fork()),在大多数 shell 中,使用:
percentage="$(printf '%8.2f\n' "$((outsideLines*100*100/totalLines))e-2")"
这一切都是在 shell 内部完成的(如果 printf 是内置的)。
不需要加载任何外部应用程序。这使得速度更快,并且 CPU 负载更少(对于一数字)。
不要尝试将其扩展到 1000 个数字,shell 不是处理文本文件的正确工具。
最近的
如果您确实需要将百分比四舍五入到最接近的偶数。 IEEE-754的默认舍入方法,我们需要在小数部分使用更多的数字。我们可以得到更多的数字,在本例中是 13(11 个小数位),方法是:
$ printf '%.2f\n' "$((outsideLines*10**15/totalLines))e-13"
62.75
$ printf '%.4f\n' "$((outsideLines*10**15/totalLines))e-13"
62.7451
$ printf '%.6f\n' "$((outsideLines*10**15/totalLines))e-13"
62.745098
尝试从 shell 的整数数学中提取超过 11 个小数位是没有意义的。双精度浮点数(通常)限制为 53 位,十进制数不超过 16 位。减去 2 位数字,因为它们用于百分比数字的整数部分,这样我们就剩下不超过 14 个可能的小数位。在较小的百分比中,该值会减少(因为数学是在整数值上执行的),因此,可能不超过 11 个小数位是可信的。
公元前
如果您需要更高的精度(非常小的百分比),则使用 shell 的整数数学是没有意义的。在这种情况下,请bc
直接使用(会降低加载和执行外部程序的速度)。
$ bc <<<"scale=30; outsidelines=$outsideLines; totallines=$totalLines; outsidelines*100/totallines"
42.857142857142857142857142857142
$ val="$(bc <<<"scale=30; outsidelines=$outsideLines; totallines=$totalLines; outsidelines*100/totallines")"
$ printf '%.8f' "$val"
42.85714286
$ printf '%.6f' "$val"
42.857143
$ printf '%.4f' "$val"
42.8571
$ printf '%.2f' "$val"
42.86
1 Shell 整数算术轮次趋向于零。此处,42.85714285714285...
被截断以42.85
删除0.00714285714285
.
² 按操作系统中的默认舍入模式进行舍入,通常舍入到最接近的值并与偶数 (银行家四舍五入(但在某些操作系统中可能是距离零最近的关系)。这是通过使用 11 个小数位来完成的。例如,当计算出的值更接近42.86
两位数时,这就是打印的值。
答案2
你可以这样做:
percentage=$(echo "scale=2; $multiplied / $totalLines" | bc)
答案3
谢谢 @斯蒂芬·查泽拉斯 指出我对变量的滥用
只是完成了 @user2323 的一点点回答:
> printf '%.2f\n' "$((outsideLines*100*100/totalLines))e-2"
> 42.85
由于“e-2”附加在末尾,因此应该在开头再加100。"$((outsideLines*100/totalLines))"
只会得到百分比的整数部分:
> printf '%.2f\n' "$((outsideLines*100/totalLines))"
> 42
,并"$((outsideLines*100/totalLines))e-2"
得到一个保留两位小数的浮点数,但不是百分比:
> printf '%.2f\n' "$((outsideLines*100/totalLines))e-2"
> 0.42