printf 是否有数字的“工程”表示法格式?

printf 是否有数字的“工程”表示法格式?

假设我有这个数字: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这样做吗?

答案1

我不知道有任何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 偏移以抵消舍入误差)

这里第一个参数被视为规模

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

注意,它bc本身不理解工程符号,你必须这样写:

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

相关内容