Скрипт оболочки и добавление больших значений в файл с разделителями

Скрипт оболочки и добавление больших значений в файл с разделителями

У меня есть скрипт, который читает файл с разделителями и добавляет 3-й элемент в файле для каждой записи. Для большинства файлов данных это работает нормально, за исключением одного. У меня есть один файл данных, в котором 193 записи. Я ожидаю получить 2028219.43 из скрипта. Вместо этого я получаю экспоненциальное число, которое, по-видимому, было округлено. Сначала я думал, что с помощью printf я получу число, но если число уже было округлено, то это не даст мне того, что я ожидаю.

Это код, который я использую для чтения файла данных с разделителями. Данные в каждой записи разделены *.:

export clm_total=$( awk -F* '{f1+=$3} END {print f1}' datafile.dat)
export new_clm_total=$(printf "%.2f" $clm_total)

Вот что отображается в журнале при запуске скрипта:

+ export clm_total=2.02822e+06
+ printf %.2f 2.02822e+06
+ export new_clm_total=2028220.00
+ echo 2028220.00

Это пример файла данных. Есть еще записи, я не думал, что нужно отображать все 193 записи:

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

Я ожидаю получить 2028219.43. Я получаю 2.02822e+06, который затем форматируется как 2028220.00.

решение1

Awk выполняет арифметические операции с числами с плавающей точкой двойной точности. Я не знаю точно, до какого предела вы получаете точные результаты для чисел с двумя десятичными знаками, но вы в пределах. Однако этоможет быть проблема, если цифры станут больше. Если вам нужно быть уверенным в получении точных результатов, либо придерживайтесь целых чисел и следите за переполнением, либо используйте , bcкоторый выполняет арифметические операции произвольной точности.

Проблема здесь в том, что awk вычисляет правильный результат, но формат печати по умолчанию приблизительный. Используйте явный формат при печати результата.

export clm_total=$( awk -F'*' '{f1+=$3} END {printf "%.2f\n", f1}' datafile.dat)

В качестве альтернативы вы можете придерживаться printпри условии, что вы измените формат печати для преобразования чисел в строки. По умолчанию это то, %.6gчто приводит к тому приближению, которое вы видите.

export clm_total=$( awk -F'*' -v CONVFMT='%.2f' '{f1+=$3} END {print f1}' datafile.dat)

решение2

При работе с числами с плавающей точкой в ​​информатике (особенно awkв вашем случае) вам следует обращать внимание на базовые механизмы, которые используются для представления ваших данных в системе.

Я считаю, что это проблема, с которой вы столкнулись в вашем конкретном случае. См. эту статью:D.3 Предостережения относительно чисел с плавающей точкой, для дополнительной информации по теме. Эта статья также была полезна для прояснения вопроса:15.2 Понимание программирования с плавающей точкой.

Насколько я могу судить, при работе с числами с плавающей точкой у вас, awkпо-видимому, есть только несколько цифр для мантиссы числа, поэтому по мере накопления чисел вы достигаете точки, в которой возникают ошибки округления и усечения, и вы теряете точность.

Пример

Здесь вы можете увидеть, когда мы переходим порог и начинаем использовать научную запись для отслеживания фактического числа.

$ 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

Связанный контент