Printf

printf 是否有數字的“工程”符號格式?

  • September 29, 2017

假設我有這個數字:105000000,在這種形式下我無法輕易看出它有多大,所以printf在提示時我嘗試使用“科學”符號:

% printf "%.3E" 105000000
1.050E+08

這更好,但我想使用“工程”符號,其中輸出格式為 10 3百萬、數十億、萬億等的冪。

例如,我想將其格式化為如下所示:

105000        => 105.0E+03      (105 thousand)
105000000     => 105.0E+06      (105 million)
105000000000  => 105.0E+09      (105 billion)
...

printf做到嗎?

我不知道有任何printf實現。請注意,POSIX 甚至不保證printf '%E\n' 123完全可以工作,因為對浮點格式的支持是可選的。

通過多種printf實現,您可以使用%'f在具有以下一種的語言環境中輸出千位分隔符:

$ LC_NUMERIC=en_GB.UTF-8 printf "%'.0f\n" 105000000
105,000,000
$ LC_NUMERIC=fr_FR.UTF-8 printf "%'.0f\n" 105000000
105 000 000
$ LC_NUMERIC=da_DK.UTF-8 printf "%'.0f\n" 105000000
105.000.000
$ LC_NUMERIC=de_CH.UTF-8 printf "%'.0f\n" 105000000
105'000'000
$ LC_NUMERIC=ps_AF.UTF-8 printf "%'.0f\n" 105000000
105٬000٬000

使用printf內置的ksh93,您還可以使用%#dK/M/G… 後綴和%#iKi/Mi/Gi 後綴:

$ printf '%#d\n' 105000000 $((2**22))
105M
4.2M
$ printf '%#i\n' 105000000 $((2**22))
100Mi
4.0Mi

(但請注意,您無法更改精度,例如從 Ki到的轉換Mi為 1000 Ki,而不是 1024 Ki,如果您習慣了 GNU 格式(如 GNU ls -lh),這可能會令人驚訝。它也僅限於整數數字高達 2 63 -1 (8Ei - 1))。

至於如何手動實現它,使用zsh

eng() {
 local n="${(j: :)argv}" exp
 zmodload zsh/mathfunc
 if ((n)) && ((exp = int(floor(log10(abs(n)) / 3)) * 3)); then
   printf '%.10ge%d\n' "n / 1e$exp" exp
 else
   printf '%.10g\n' "$n"
 fi
}

進而:

$ eng 123
123
$ eng 12345
12.345e3
$ eng 0.000000123123
123.123e-9
$ eng 1. / -1234
-810.3727715e-6

請注意,zsh與許多其他語言一樣,涉及浮點數的操作是在浮點算術中完成的(使用您的處理器double類型),而那些僅涉及整數的操作是在整數算術中完成的(使用您的處理器long類型)。這有一些含義,例如:

$ eng 1  / -1234
0
$ eng 1. / -1234
-810.3727715e-6

但是也:

$ eng 1 \*{2..28}.  # factorial 28
304.8883446e27
$ eng 1 \*{2..28}
-5.968160533e18  # 64bit signed integer overflow

(雖然這不是特定於該eng功能)

或者作為使用 POSIX 的 POSIX shell 函式bc,因此允許任意精度:

eng() (
 IFS=" "
 scale=$1; shift
 bc -l << EOF |
 s = scale = $scale
 if (scale < 20) s = 20
 n = $*
 if (n != 0) {
   scale = s
   a = n; if (a < 0) a = -a
   e = l(a) / l(10) / 3 + 10 ^ -15
   if (e < 0) e -= 1
   scale = 0
   e = e / 1 * 3
   scale = s
   if (scale <= -e) scale = 1 - e
   n = n / 10^e
   scale = $scale
 }
 n/1
 if (e != 0) e
EOF
   sed '
     :1
     /\\$/{
       N;b1
     }
     s/\\\n//g
     /\./s/0*$//
     s/\.$//
     $!N
     s/\n/e/'
)

(在計算 n 值(如 0.001)的指數 log10(n) 時,偏移 1e-15 以抵消舍入誤差)

這裡將第一個參數作為scale

$ eng 2 1/3
330e-3
$ eng 20 1/3
333.33333333333333333e-3

請注意,bc它本身並不理解工程符號,您必須編寫:

$ eng 20 "1.123123 * 10^2000"
112.3123e1998

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