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