如何正确使用bc将百分比值转换为浮点格式?

如何正确使用bc将百分比值转换为浮点格式?

我有 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

相关内容