
У меня есть скрипт, который читает файл с разделителями и добавляет 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