
Eu tenho um script que lê um arquivo delimitado e adiciona o terceiro elemento do arquivo para cada registro. Para a maioria dos arquivos de dados, isso funciona bem, exceto um. Eu tenho um arquivo de dados onde existem 193 registros no arquivo de dados. Espero obter 2028219,43 de volta do script. Em vez disso, recebo de volta um número exponencial que parece ter sido arredondado. A princípio pensei que usando printf conseguiria o número, mas se o número já tiver sido arredondado não vai me devolver o que estou esperando.
Este é o código que estou usando para ler o arquivo de dados delimitado. Os dados em cada registro são delimitados por um *.:
export clm_total=$( awk -F* '{f1+=$3} END {print f1}' datafile.dat)
export new_clm_total=$(printf "%.2f" $clm_total)
Isto é o que aparece no log quando executo o script:
+ export clm_total=2.02822e+06
+ printf %.2f 2.02822e+06
+ export new_clm_total=2028220.00
+ echo 2028220.00
Esta é uma amostra do arquivo de dados. Existem mais registros, não achei necessário exibir todos os 193 registros:
CLM*123456789*4820.9***13:A:1**A*Y*Y
CLM*123698547*3642.05***13:A:7**A*Y*Y
CLM*147852369*579.25***13:A:1**A*Y*Y
CLM*789654123*929.8***13:A:1**A*Y*Y
O que estou esperando de volta é 2028219.43 O que estou recebendo de volta é 2.02822e+06, que então é formatado como 2028220.00
Responder1
Awk realiza aritmética em números de ponto flutuante de precisão dupla. Não sei exatamente até que limite você obtém resultados exatos para números com duas casas decimais, mas você está dentro do alcance. No entanto, issopode ser um problema se os números aumentarem. Se você precisar obter resultados exatos, atenha-se a números inteiros e tome cuidado com o estouro ou use bc
o que executa aritmética de precisão arbitrária.
O problema aqui é que o awk está computando o resultado correto, mas o formato de impressão padrão é aproximado. Use um formato explícito ao imprimir o resultado.
export clm_total=$( awk -F'*' '{f1+=$3} END {printf "%.2f\n", f1}' datafile.dat)
Como alternativa, você pode seguir, print
desde que altere o formato de impressão para converter números em strings. O padrão é %.6g
which resulta na aproximação que você está vendo.
export clm_total=$( awk -F'*' -v CONVFMT='%.2f' '{f1+=$3} END {print f1}' datafile.dat)
Responder2
Ao lidar com matemática de ponto flutuante na ciência da computação (especificamente awk
no seu caso), você deve prestar atenção aos mecanismos subjacentes que estão sendo usados para representar seus dados dentro do sistema.
Acredito que esse seja o problema que você está enfrentando no seu caso específico. Veja este artigo:D.3 Advertências sobre números de ponto flutuante, para obter informações adicionais sobre o assunto. Este artigo também foi útil para esclarecer o assunto:15.2 Compreendendo a programação de ponto flutuante.
O melhor que posso dizer ao lidar com números de ponto flutuante awk
parece que você tem apenas alguns dígitos disponíveis para a parte da mantissa do número, então, à medida que você continua acumulando números, chega ao ponto em que erros de arredondamento e truncamento são introduzidos e você está perdendo precisão.
Exemplo
Você pode ver aqui quando ultrapassamos o limite e começamos a usar a notação científica para rastrear o número real.
$ seq -f "%f" 1413 | awk '{f1+=$1+0.4} END {print f1}'
999556
$ seq -f "%f" 1414 | awk '{f1+=$1+0.4} END {print f1}'
1.00097e+06