Нужны полные десятичные знаки в сумме awk при суммировании десятичных значений

Полагается ли сценарий на такие зависимости, как Python или Ruby? Им также потребуются разрешения. Вы можете проверить это с помощью:

 run-parts /etc/cron.hourly -v

и проверьте, нет ли ошибок. Запустите от имени пользователя root, так как это uid, с которым работает cron.

2
20.02.2020, 18:40
3 ответа

При печати не -целые числа преобразуются в десятичное строковое представление с использованием специальной переменной OFMT, которая по умолчанию содержит спецификацию формата printf(%.6g). Вы можете изменить его на %.17g, чтобы получить максимальную точность двоичных чисел двойной точности IEEE 754 с плавающей запятой (, которая используется внутри большинства awkреализаций в большинстве систем ). Другая переменная(CONVFMT)используется в других случаях, когда числа с плавающей запятой неявно преобразуются в строки (, например, когда вы объединяете число с чем-то другим )

.

Вы не получите большей точности с этими двойниками, нет смысла выходить за пределы 17. Уже с 17 вы, вероятно, увидите некоторые артефакты. 15 значащих цифр могут быть лучше, если вам не нужна такая точность.

$ awk -v OFMT=%.17g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981000000008
$ awk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981

Хотя OFMTвлияет на все печатаемые числа с плавающей запятой, вы также можете использовать printfнапрямую для печати чисел с требуемой точностью.

$ awk  -F ',' '{SUM+=$2};END{printf "%.15g\n", SUM}' < file
6.7088981

Реализация GNU awk, начиная с версии 4.1.0, также может быть скомпилирована с поддержкой арифметики произвольной точности (, см. info gawk 'Arbitrary Precision Arithmetic'). Если это так в вашей системе, вы также можете сделать:

gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}' < file

Пример:

$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}'
999999999999999945575230987042816
$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}'
1000000000000000000000000000000001.00000000001

Другой подход здесь может состоять в том, чтобы использовать bc(, предполагая, что эти числа всегда выражаются так (0.001, а не 1e-3, например)):

<file tail -n+2 | # skip header
  cut -d, -f2   | # extract second field
  paste -sd + - | # join input lines with +
  bc

Количество цифр после .будет максимальным в любой входной записи.

4
28.04.2021, 23:22

GNU datamash отображает число с нужной точностью с настройками вывода по умолчанию:

$ datamash --header-in -t, sum 2 < test.txt
6.7088981

Или awk с другим OFMTболее точным:

$ awk -F, -v OFMT='%.10g' '{sum += $2} END { print sum }' test.txt
6.7088981

, но см. Is Floating -Point Math Broken? . Количество цифр после запятой, когда число с плавающей запятой отображается с основанием 10, не всегда соответствует представлению IEEE754 с основанием 2, которое (использует большинство )компьютеров.

2
28.04.2021, 23:22

Как уже обсуждалось, арифметика с плавающей запятой представляет собой проблему при попытке получить интуитивно понятный ответ, но если вы знаете, что ваш ввод может содержать до, скажем, 3 цифр перед «.» и, скажем, 9 после него, вы можете преобразовать свои числа в десятичные числа, используя манипуляции со строками, затем суммировать их, чтобы избежать проблемы с арифметикой с плавающей запятой, а затем снова преобразовать результат обратно в FP перед печатью, например.:

$ cat tst.awk
BEGIN {
    FS = ","
    bef = 3
    aft = 9
}
NR>1 {
    split($2,f,".")
    val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
    gsub(/ /,0,val)
    sum += val
}
END {
    sub(".{"aft"}$",".&",sum)
    sub(/0+$/,"",sum)
    print sum
}

$ awk -f tst.awk file
6.7088981

Если 3 и/или 9 не являются для вас достаточно большими числами, выберите другие числа или выполните подход за 2 -прохода, который вычисляет максимальное значение для каждого на первом проходе, например.:

$ cat tst.awk
BEGIN { FS = "," }
FNR==1 { next }
{ split($2,f,".") }
NR==FNR {
    bef = (length(f[1]) > bef ? length(f[1]) : bef)
    aft = (length(f[2]) > aft ? length(f[2]) : aft)
    next
}
{
    val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
    gsub(/ /,0,val)
    sum += val
}
END {
    sub(".{"aft"}$",".&",sum)
    sub(/0+$/,"",sum)
    print sum
}

$ awk -f tst.awk file file
6.7088981
1
28.04.2021, 23:22

Теги

Похожие вопросы