Печать длинных целых чисел в awk - PullRequest
8 голосов
/ 14 января 2012

У меня есть файл канала с разделителями каналов, который имеет несколько полей. Поскольку мне нужно всего несколько, я подумал об использовании awk для захвата их в целях тестирования. Однако я заметил, что printf меняет значение, если я использую "%d". Работает нормально, если я использую "%s".

Пример файла фида:

[jaypal:~/Temp] cat temp

302610004125074|19769904399993903|30|15|2012-01-13 17:20:02.346000|2012-01-13 17:20:03.307000|E072AE4B|587244|316|13|GSM|1|SUCC|0|1|255|2|2|0|213|2|0|6|0|0|0|0|0|10|16473840051|30|302610|235|250|0|7|0|0|0|0|0|10|54320058002|906|722310|2|0||0|BELL MOBILITY CELLULAR, INC|BELL MOBILITY CELLULAR, INC|Bell Mobility|AMX ARGENTINA SA.|Claro aka CTI Movil|CAN|ARG|

Я заинтересован в захвате second column, что составляет 19769904399993903.

Вот мои тесты:

[jaypal:~/Temp] awk -F"|" '{printf ("%d\n",$2)}' temp
19769904399993904   # Value is changed

Тем не менее, следующие два теста работают нормально -

[jaypal:~/Temp] awk -F"|" '{printf ("%s\n",$2)}' temp
19769904399993903   # Value remains same

[jaypal:~/Temp] awk -F"|" '{print $2}' temp
19769904399993903   # Value remains same

Так что это предел "%d" не способен обрабатывать длинные целые числа. Если дело обстоит так, почему он добавляет один к числу вместо того, чтобы обрезать это?

Я пробовал это с BSD и GNU версиями awk.

Информация о версии:

[jaypal:~/Temp] gawk --version
GNU Awk 4.0.0
Copyright (C) 1989, 1991-2011 Free Software Foundation.

[jaypal:~/Temp] awk --version
awk version 20070501

Ответы [ 6 ]

8 голосов
/ 27 декабря 2014

Начиная с GNU awk 4.1 , вы можете использовать --bignum или -M

$ awk 'BEGIN {print 19769904399993903}'
19769904399993904

$ awk --bignum 'BEGIN {print 19769904399993903}'
19769904399993903

§ Параметры командной строки

5 голосов
/ 14 января 2012

Я полагаю, что основной числовой формат в этом случае является двойным IEEE. Таким образом, измененное значение является результатом ошибок точности с плавающей запятой. Если на самом деле необходимо обрабатывать большие значения как числа и поддерживать точную точность, может быть лучше использовать что-то вроде Perl, Ruby или Python, которые имеют возможности (возможно, через расширения) для обработки арифметики произвольной точности.

4 голосов
/ 15 января 2012

ОБНОВЛЕНИЕ : последние версии GNU awk поддерживают арифметику произвольной точности.См. Руководство GNU awk для получения дополнительной информации.

ОРИГИНАЛЬНОЕ СОДЕРЖАНИЕ ПОЧТЫ: XMLgawk поддерживает произвольную арифметику точности для чисел с плавающей запятой.Так, если установка xgawk является опцией:

zsh-4.3.11[drado]% awk --version |head -1; xgawk --version | head -1
GNU Awk 4.0.0
Extensible GNU Awk 3.1.6 (build 20080101) with dynamic loading, and with statically-linked extensions

zsh-4.3.11[drado]% awk 'BEGIN {
  x=665857
  y=470832
  print x^4 - 4 * y^4 - 4 * y^2
  }'
11885568

zsh-4.3.11[drado]% xgawk -lmpfr 'BEGIN {
  MPFR_PRECISION = 80
  x=665857
  y=470832
  print mpfr_sub(mpfr_sub(mpfr_pow(x, 4), mpfr_mul(4, mpfr_pow(y, 4))), 4 * y^2)
  }'
1.0000000000000000000000000
2 голосов
/ 26 февраля 2014

На этот ответ частично уже ответили @Mark Wilkins и @Dennis Williamson, но я обнаружил, что наибольшее 64-разрядное целое число, которое может быть обработано без потери точности, равно 2 ^ 53.Например, справочная страница awk http://www.gnu.org/software/gawk/manual/gawk.html#Integer-Programming

(извините, если мой ответ слишком старый. Думаю, я все же расскажу следующему человеку, прежде чем они потратят на это слишком много времени)

1 голос
/ 14 января 2012

Вы сталкиваетесь с Проблемы с отображением с плавающей точкой в ​​Awk . Я не думаю, что вы можете найти обходной путь в рамках awk для точного выполнения арифметики с огромными числами.

Единственно возможный (и грубый) способ, который я могу придумать, - это разбить огромное число на меньшие куски, выполнить свою математику и присоединиться к ним снова или лучше, но при этом использовать языки сценариев Perl / PHP / TCL / bsh и т. Д., Которые являются более мощными, чем AWK.

0 голосов
/ 15 января 2016

Используя nawk в Solaris 11, я преобразовываю число в строку, добавляя (конкатенируя) нулевое значение в конец, а затем использую %15s в качестве строки формата:

printf("%15s\n", bignum "")   
...