Bash

如何在bash中用正好2個有效數字格式化浮點數?

  • November 27, 2018

我想用 bash 中的兩個有效數字列印浮點數(可能使用 awk、bc、dc、perl 等常用工具)。

例子:

  • 76543 應列印為 76000
  • 0.0076543 應列印為 0.0076

在這兩種情況下,有效數字都是 7 和 6。我已經閱讀了一些類似問題的答案,例如:

如何在shell中舍入浮點數?

Bash限制浮點變數的精度

但答案集中在限制小數位數(例如bc命令 withscale=2printfcommand with %.2f)而不是有效數字。

有沒有一種簡單的方法可以用 2 個有效數字格式化數字,還是我必須編寫自己的函式?

這個對第一個連結問題的答案在結尾處幾乎是一次性的:

另請參閱%g四捨五入到指定數量的有效數字。

所以你可以簡單地寫

printf "%.2g" "$n"

(但請參閱下面關於小數分隔符和語言環境的部分,並註意非 Bashprintf不需要支持%fand %g)。

例子:

$ printf "%.2g\n" 76543 0.0076543
7.7e+04
0.0077

當然,您現在有尾數指數表示而不是純十進制,因此您需要轉換回來:

$ printf "%0.f\n" 7.7e+06
7700000

$ printf "%0.7f\n" 7.7e-06
0.0000077

將所有這些放在一起,並將其包裝在一個函式中:

# Function round(precision, number)
round() {
   n=$(printf "%.${1}g" "$2")
   if [ "$n" != "${n#*e}" ]
   then
       f="${n##*e-}"
       test "$n" = "$f" && f= || f=$(( ${f#0}+$1-1 ))
       printf "%0.${f}f" "$n"
   else
       printf "%s" "$n"
   fi
}

(注意 - 這個函式是用可移植(POSIX)shell 編寫的,但假定它printf處理浮點轉換。Bash 有一個內置的printf,所以你在這裡沒問題,而且 GNU 實現也可以工作,所以大多數 GNU /Linux 系統可以安全地使用 Dash)。

測試案例

radix=$(printf %.1f 0)
for i in $(seq 12 | sed -e 's/.*/dc -e "12k 1.234 10 & 6 -^*p"/e' -e "y/_._/$radix/")
do
   echo $i "->" $(round 2 $i)
done

測試結果

.000012340000 -> 0.000012
.000123400000 -> 0.00012
.001234000000 -> 0.0012
.012340000000 -> 0.012
.123400000000 -> 0.12
1.234 -> 1.2
12.340 -> 12
123.400 -> 120
1234.000 -> 1200
12340.000 -> 12000
123400.000 -> 120000
1234000.000 -> 1200000

關於小數分隔符和語言環境的說明

上面的所有工作都假設基數字元(也稱為小數分隔符)是.,就像在大多數英語語言環境中一樣。其他語言環境改為使用,並且一些 shell 具有尊重語言環境,的內置功能。printf在這些 shell 中,您可能需要設置LC_NUMERIC=C強制使用.as radix 字元,或寫入/usr/bin/printf以防止使用內置版本。後者由於(至少某些版本)似乎總是使用 解析參數.,但使用目前語言環境設置列印這一事實而變得複雜。

引用自:https://unix.stackexchange.com/questions/265119