Печать длинных целых чисел в 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, которые имеют возможности (возможно, через расширения) для обработки арифметики с произвольной точностью.
Ответ 2
Начиная с GNU awk 4.1 вы можете использовать --bignum
или -M
$ awk 'BEGIN {print 19769904399993903}'
19769904399993904
$ awk --bignum 'BEGIN {print 19769904399993903}'
19769904399993903
Параметры командной строки
Ответ 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 "")