To convert an string (that looks like a number) in awk:
- It could be assigned to a variable as a program constant.
- The function
strtonum()
could convert the text.
- Awk could be called with the option
-n
(now deprecated).
Once converted to a number, in most awk (gawk, mawk, nawk, bawk), it is stored as a 64 bit floating point. Those numbers could include only 53 bits of mantissa. Any additional bits are truncated. That allows for 53/4 = 13 hexadecimal digits (well, technically, 1 as the integer and 13 digits after the dot).
The hexadecimal you used 0xffffffffbb6002e0
is this in binary:
bc <<<"obase=2;ibase=16;FFFFFFFFBB6002E0"
1111111111111111111111111111111110111011011000000000001011100000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<== up to here 53 bits.
All fractional numbers and most integers in awk are stored as a floating number. The only other option with GNU awk is to use arbitrary precision, the -M
option. Using that option means that immediately all integers are represented with as many digits as needed and as the computer memory allows.
$ awk -M 'BEGIN{print 3^4^5}'
373391848741020043532959754184866588225409776783734007750636931722079040617265251229993688938803977220468765065431475158108727054592160858581351336982809187314191748594262580938807019951956404285571818041046681288797402925517668012340617298396574731619152386723046235125934896058590588284654793540505936202376547807442730582144527058988756251452817793413352141920744623027518729185432862375737063985485319476416926263819972887006907013899256524297198527698749274196276811060702333710356481
Which will allow your integer to be used without any problem as long as it is used only in calculations with other integers. No division.
$ awk -M 'BEGIN{x=strtonum(0xffffffffbb6002e0); y=x+234; z=x/77; printf("%x\n%x\n%f\n",x,y,z)}'
ffffffffbb6002e0
ffffffffbb6003ca
239568104838418400.000000
The correct result from x/77
should be 239568104838418388.36363636363636363636
according to bc.
If you need to have numbers with fractional part that require more than 53 bits (which is the precision retained even with -M
) you need to make the variable PREC
bigger than 53 as needed:
$ awk -M -vPREC=200 'BEGIN{x=strtonum(0xffffffffbb6002e0); y=x+234; z=x/77; printf("%x\n%x\n%f\n",x,y,z)}'
ffffffffbb6002e0
ffffffffbb6003ca
239568104838418388.363636
Hope that this helps.
Code for all claims:
Using the shell for portability and using the %a
that is closer to the internal representation of floats, 53 bits is 13 digits:
$ dash -c 'printf "%a\n" 0x1.12345678901234567890123'
0x1.1234567890123p+0
Other shells (and some awk) might use an 80 bit number with 64 bit mantissa which could use up to 16 digits:
ksh -c 'printf "%a\n" 0x1.12345678901234567890123'
0x1.1234567890123456000000000000p+0
Awk is limited to what it could accept as hexadecimal (as a program constant (x=
)).
$ awk 'BEGIN { x=0x1fffffffffffff ; y=0x3fffffffffffff; printf("%18s %16x\n%18s %16x\n", x, x+0,y,y+0); }'
9007199254740991 1fffffffffffff
18014398509481984 40000000000000
$ mawk -vx=$(printf '%d\n' 0xffffffff) 'BEGIN{y=x*2;printf("%18s %16x\n%18s %16x\n", x, x+0,y,y+0); }'
4294967295 7fffffff
8.58993e+09 7fffffff
$ bawk 'BEGIN { x=2147483647 ; y=x*2+1; printf("%18s %16x\n%18s %16x\n", x, x+0,y,y+0); }'
2147483647 7fffffff
4294967295 80000000
And, input from a file and/or the user can not accept hexadecimal numbers unless the -n
option (which is already deprecated) or the function strtonum()
(recommended) is used:
$ awk '{x=$1; printf "%s %x\n",x,x}' <<<0x123
0x123 0
$ awk -n '{x=$1; printf "%s %x\n",x,x}' <<<0x123
0x123 123
$ awk -n '{x=strtonum($1); printf "%s %x\n",$1,x}' <<<0x123
0x123 123
On the first input awk only reads the first 0
and rejects everything after the x
because it looks like a word. It works correctly on the other two cases.
So, we must use a decimal number to simplify things for awk. If your printf is limited, use bc:
$ val=$(printf "%d" 0x1234567890)
$ awk -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
78187493520 1234567890
$ val=$(bc <<<'ibase=16;1234567890')
$ awk -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
78187493520 1234567890
But still, awk is limited:
$ val=$(bc <<<'ibase=16; 12345678901234')
$ awk -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
5124095575331380 12345678901234
$ val=$(bc <<<'ibase=16; 123456789012345')
$ awk -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
81985529205302085 123456789012340
Here it cuts the last 5
, as it could not be represented in a float of 53 bits.
The ability to process large numbers improve if the bignum
(-M
) option for arbitrary precision is used, but only for integers:
$ val=$(bc <<<'ibase=16; 12345678901234567890123456789')"
$ awk -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
5907679980460342222050878921467785 5.90768e+33
$ awk -M -vx="$val" 'BEGIN{printf "%s %x\n", x,x}'
5907679980460342222050878921467785 12345678901234567890123456789
If you actually need to work with big numbers and long decimals, you need to also change the PREC used (53 by default).
$ awk -M -vx='12345678901234567890123456789' 'BEGIN{printf "%s \n%f\n", x,x/100}'
12345678901234567890123456789
123456789012345678152597504.000000
$ awk -M -vPREC=500 -vx='12345678901234567890123456789' 'BEGIN{printf "%s \n%f\n", x,x/100}'
12345678901234567890123456789
123456789012345678901234567.890000