Печать длинных целых чисел в awk

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

Ответ 1

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

Ответ 3

UPDATE. Последние версии 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

Ответ 4

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

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

Ответ 5

Вы столкнулись с Awk Floating Point Representation Issues. Я не думаю, что вы можете найти работу в рамках awk, чтобы точно выполнить арифметику на огромных количествах.

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

Ответ 6

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

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