O printf tem um formato de notação de “engenharia” para números?

5

Digamos que eu tenha este número: 105000000, neste formulário não consigo ver facilmente o tamanho dele, portanto, usando printf em um prompt, tento usar a notação "científica":

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

Isso é melhor, mas eu gostaria de usar uma notação de "engenharia" onde a saída é formatada em potências de 10 3 milhões, bilhões, trilhões etc.
por exemplo. Quero formatá-lo para ficar assim:

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

pode printf fazer isso?

    
por the_velour_fog 28.09.2017 / 03:33

1 resposta

6

Eu não sei de nenhuma implementação de printf . Note que o POSIX nem mesmo garante printf '%E\n' 123 para funcionar, já que o suporte para formatos de ponto flutuante é opcional.

Com várias implementações de printf , você pode usar %'f para gerar milhares de separadores em códigos de idioma que possuem um:

$ 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

Com o printf incorporado em ksh93 , você também pode usar %#d para K / M / G ... sufixos e %#i para Ki / Mi / Gi:

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

(note que não é possível alterar a precisão e a transição de Ki para Mi , por exemplo, é de 1000 Ki, não 1024 Ki, o que pode ser surpreendente se você estiver acostumado com o formato GNU (como no GNU ls -lh ). Ela também é limitada a números inteiro até 2 63 -1 (8Ei - 1)).

Sobre como implementá-lo manualmente, com 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
}

E então:

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

Note que em zsh como em muitas outras linguagens, as operações envolvendo floats são feitas em aritmética de ponto flutuante (com o tipo double do seu processador) enquanto aquelas envolvendo inteiros apenas são feitas em aritmética inteira (com long do seu processador tipo). Isso tem algumas implicações como:

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

mas também:

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

(embora não seja específico para essa função eng )

Ou como uma função de shell POSIX usando POSIX bc , permitindo assim a precisão arbitrária:

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/'
)

(com um deslocamento de 1e-15 para compensar erros de arredondamento ao calcular log10 (n) para o expoente para valores de n como 0,001)

Aqui, o primeiro argumento é considerado como a escala :

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

Note que bc em si não entende a notação de engenharia, você tem que escrever:

$ eng 20 "1.123123 * 10^2000"
112.3123e1998
    
por 28.09.2017 / 12:30

Tags