Shell script e adição de valores grandes em um problema de arquivo delimitado

Shell script e adição de valores grandes em um problema de arquivo delimitado

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 bco 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, printdesde que altere o formato de impressão para converter números em strings. O padrão é %.6gwhich 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 awkno 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 awkparece 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

informação relacionada