Usando zsh
:
width=7.25; echo $(( width * 2 ))
: a saída é14.5
width=7.5; echo $(( width * 2 ))
: a saída é15.
, com um ponto final
Isso parece ser uma coisa muito básica, mas ainda não descobri. Como posso obter apenas 15
, sem o ponto final, no segundo exemplo?
Para se livrar do ponto final, use a
${name%pattern}
forma de expansão de parâmetro .Isso não ajudará se o resultado não for um número inteiro devido a aproximações.
Se você não gosta desse resultado:
zsh
é um dos três shells do tipo Bourne (com ksh93 e yash) que suportam aritmética de números inteiros e aritmética de ponto flutuante em suas expressões aritméticas (dentro de(( ... ))
, e a maioria dos componentes internos que aceitam números como argumentos, como em ksh$(( ... ))
,${array[...]}
de onde vêm esses recursos).Essas expressões aritméticas são moldadas de acordo com a linguagem C e seguem basicamente as mesmas regras¹. Em particular, uma operação que envolve apenas números inteiros produz um número inteiro. E assim que houver um carro alegórico, há uma promoção para o carro alegórico.
Como em C, um literal numérico é considerado ponto flutuante se contiver a
.
ou usar a notação de engenharia (1e-2
...) ou for uma das variantes de NaN ou Infinity² e inteiro caso contrário, portanto, anexar a.
a uma sequência de dígitos decimais é a maneira mais fácil de garantir que ela seja considerada uma constante flutuante em vez de uma constante inteira.Por exemplo, em C como em zsh/ksh93³/yash,
2 / 4 * 6.
produz 0. porque produz o inteiro2 / 4
0 e produz o float.0 * 6.
0.
Embora
2. / 4 * 6
produza3.
porque2. / 4
é uma operação de ponto flutuante porque pelo menos um dos operandos é um ponto flutuante que produz 0,5 e0.5 * 6
também é uma operação flutuante.O mesmo em ksh93:
Ou zsh:
Exceto que
zsh
preserva a informação de que a expressão gerou um float adicionando that.
. Não acrescenta isso.
se o número já for uma representação flutuante por outros motivos, como:De forma mais geral, quando zsh fornece uma representação de texto decimal de um número de ponto flutuante (que ele armazena internamente como o
double
tipo C), ele tenta fornecer uma representação completamente fiel, de modo que, se for usado posteriormente em outra expressão aritmética, não há perda de informações, incluindo tipo (inteiro versus flutuante) e precisão.É por isso que além de adicionar isso
.
para desambiguar float de inteiros, ele usa o%.17g
sprintf()
formato ao calcular a representação de texto inteiro decimal do número, já que 17 dígitos é o número mínimo de dígitos decimais necessários no caso geral para garantir a precisão total de um 64 bits O número binário de precisão dupla IEEE 754 é mantido. E tanta precisão tem a desvantagem de expor os erros incorridos na conversão entre decimal e binário.Isso é discutido detalhadamente em Por que $((0.1)) se expande para 0,1000000000000001 em zsh?
Agora, para consumo humano, você geralmente não precisa ver isso
.
para saber que é para ser um número de ponto flutuante, não que você se importe se é para ser ou não. E você não precisa ter tanta precisão, então, para fins de exibição e apenas para consumo humano, é melhor usarprintf
ouprint -f
formatar o número.Imprimiria o valor da largura formatado da mesma forma que,
awk
por exemplo, formataria-o com 6 dígitos de precisão (também mudando para9.9999e-05
ou1e06
para números abaixo de 1e-4 ou acima de 1e6)Aqui com a diferença de que os números não passam pelo
OFMT
formato se forem considerados inteiros.Usar:
Se você deseja preservar o máximo de precisão possível e ao mesmo tempo remover os
.
números que não possuem parte decimal.Assim como no ksh, também é possível declarar uma variável com:
Para
$x
ser expresso em notação de ponto flutuante com 2 dígitos após o ponto final quando expandido (como na%.2f
printf
diretiva) e$y
para ser expresso em notação de engenharia com 6 dígitos significativos (como na%.6e
printf
diretiva), mas não acho que haja um equivalente para%g
.Observe que o valor da variável ainda é armazenado internamente com total precisão, esses atributos
-F
/-E
afetam apenas as expansões.¹ Exceto
zsh
ao contrário, ksh tem algumas incompatibilidades quando se trata de precedência de operador, embora isso não seja relevante para esta questão.² Observe que, ao contrário de ksh93, yash ou C99, ele não suporta hexadecimais de ponto flutuante, como
0xDead.Beef
ou0x123p-4
ainda.³ Tenha cuidado, pois ksh93 respeita o caractere de raiz decimal do código do idioma; em um local onde está em
,
vez de.
, isso teria que ser escrito2 / 4 * 6,