我一直在 AWK ( gawk
) 中尝试使用十六进制数字,但有时当我使用 eg 打印它们时printf
,它们会被打印,并屏蔽掉一些 LSB,如下例所示:
awk 'BEGIN { x=0xffffffffbb6002e0; printf("%x\n", x); }'
ffffffffbb600000
为什么我会出现这种行为?我该如何纠正?
我gawk
在 Debian Buster 10 上使用。
答案1
默认情况下,AWK 中的数字是浮点数,并且您的值超出了可用的精度。0xffffffffbb6002e0
最终以0 10000111110 1111111111111111111111111111111101110110110000000000
IEEE-754 二进制 64 形式表示(双精度) 格式,表示整数值0xffffffffbb600000
。注意低 12 位的变化,四舍五入为零。
转换后产生舍入误差的最小正整数double
是 2 53 + 1。数字越大,adouble
可以表示的值之间的差距越大。 (步骤为 2,然后是 4,然后是 8,等等;这就是数字的低十六进制数字四舍五入到零的原因。)
使用 GAWK,如果它是使用 MPFR 和 MP 构建的(Debian 中就是这种情况),您可以强制任意精度而是使用以下-M
选项:
$ awk -M 'BEGIN { x=0xffffffffbb6002e0; printf("%x\n", x); }'
ffffffffbb6002e0
对于计算,这将默认为与 IEEE-754 双精度相同的 53 位精度,但PREC
可以使用变量来控制它。有关详细信息,请参阅上面链接的手册。
对于需要超过默认精度的大整数和浮点值的处理存在差异,这可能会导致令人惊讶的行为;大整数可以使用-M
及其默认设置正确解析(只有后续计算受 影响PREC
),而浮点值则以解析时定义的精度存储,这意味着PREC
需要预先进行适当设置:
# Default settings, integer value too large to be exactly represented by a binary64
$ awk 'BEGIN { v=1234567890123456789; printf "%.20f\n", v }'
1234567890123456768.00000000000000000000
# Forced arbitrary precision, same integer value stored exactly without rounding
$ awk -M 'BEGIN { v=1234567890123456789; printf "%.20f\n", v }'
1234567890123456789.00000000000000000000
# Default settings, floating-point value requiring too much precision
$ awk 'BEGIN { v=123456789.0123456789; printf "%.20f\n", v }'
123456789.01234567165374755859
# Forced arbitrary precision, floating-point parsing doesn’t change
$ awk -M 'BEGIN { v=123456789.0123456789; printf "%.20f\n", v }'
123456789.01234567165374755859
# Forced arbitrary precision, PREC set in the BEGIN block, no difference
$ awk -M 'BEGIN { PREC=94; v=123456789.0123456789; printf "%.20f\n", v }'
123456789.01234567165374755859
# Forced arbitrary precision, PREC set initially
$ awk -M -vPREC=94 'BEGIN { v=123456789.0123456789; printf "%.20f\n", v }'
123456789.01234567890000000000
读取输入值时,AWK 只将十进制值识别为数字;要处理非十进制值(八进制或十六进制),应使用GAWK的strtonum
功能。
答案2
要在 awk 中转换字符串(看起来像数字):
- 它可以作为程序常量分配给变量。
- 该函数
strtonum()
可以转换文本。 - 可以使用该选项调用 awk
-n
(现已弃用)。
一旦转换为数字,在大多数 awk(gawk、mawk、nawk、bawk)中,它会存储为 64 位浮点数。这些数字只能包含 53 位尾数。任何额外的位都会被截断。这允许 53/4 = 13 个十六进制数字(从技术上讲,1 作为整数,点后有 13 个数字)。
您使用的十六进制0xffffffffbb6002e0
是二进制的:
bc <<<"obase=2;ibase=16;FFFFFFFFBB6002E0"
1111111111111111111111111111111110111011011000000000001011100000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<== up to here 53 bits.
awk 中的所有小数和大多数整数都存储为浮点数。 GNU awk 的唯一其他选项是使用任意精度,该-M
选项。使用该选项意味着所有整数都会立即根据需要和计算机内存允许的数量来表示。
$ awk -M 'BEGIN{print 3^4^5}'
373391848741020043532959754184866588225409776783734007750636931722079040617265251229993688938803977220468765065431475158108727054592160858581351336982809187314191748594262580938807019951956404285571818041046681288797402925517668012340617298396574731619152386723046235125934896058590588284654793540505936202376547807442730582144527058988756251452817793413352141920744623027518729185432862375737063985485319476416926263819972887006907013899256524297198527698749274196276811060702333710356481
只要您的整数仅用于与其他整数的计算,这将允许您的整数使用没有任何问题。没有划分。
$ 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
正确的结果x/77
应该是239568104838418388.36363636363636363636
根据bc。
如果您需要具有小数部分的数字需要超过 53 位(这是即使使用 保留的精度-M
),您需要根据需要使变量PREC
大于 53:
$ 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
希望这有帮助。
所有索赔的代码:
使用 shell 来实现可移植性,并使用%a
更接近浮点数内部表示的 ,53 位就是 13 位数字:
$ dash -c 'printf "%a\n" 0x1.12345678901234567890123'
0x1.1234567890123p+0
其他 shell(和一些 awk)可能使用 80 位数字和 64 位尾数,最多可以使用 16 位数字:
ksh -c 'printf "%a\n" 0x1.12345678901234567890123'
0x1.1234567890123456000000000000p+0
awk 仅限于它可以接受的十六进制(作为程序常量 ( 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
并且,来自文件和/或用户的输入不能接受十六进制数字,除非选项-n
(已被弃用strtonum()
) 或使用函数(推荐):
$ 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
在第一个输入上,awk 只读取第一个输入0
并拒绝后面的所有内容,x
因为它看起来像一个单词。它在其他两种情况下工作正常。
因此,我们必须使用十进制数来简化 awk 的事情。如果你的 printf 有限,请使用 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
但 awk 仍然有局限性:
$ 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
这里它剪切了最后一个5
,因为它无法用 53 位浮点数表示。
如果使用任意精度的bignum
( ) 选项,则处理大数的能力会提高,但仅适用于整数:-M
$ 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
如果您确实需要处理大数字和长小数,则需要还更改使用的 PREC(默认为 53)。
$ 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
答案3
我处理 gawk、mawk134 和 mawk2 不同精度级别的方法是编写一个包装函数来封装子 shell gawk 执行。因此,每当任何函数检测到输入高于其当前环境的精度时,它都会通过此包装器调用自身,以便gawk -M
在子 shell 中执行它,并使用 getline 返回结果(通过包装器封装,这也会修剪最后一个尾随 \n)。
说我是否想做 的质因数分解2^190 - 1
。我引用它们并将其作为字符串传递到我的函数中,因此子 shell 仍然可以看到所有内容,而不是进行精确的预修剪,从而使子 shell 的意义无效。
作为包装器的一部分,我还对需要为子 shell 声明的 PREC 进行最佳猜测,然后在其上添加一些固定填充以确保确定。
答案4
@user232326:mawk
确实不是与任何其他相比,数字较少或精度较低non-bignum
awk
echo '0x1' | mawk '{ __ = (_+=_^=_<_)^_^_+_
_ = $___
do { print _
_ = (_) "F" } while(--__) }' |
mawk '$++NF = +$_' CONVFMT='%.f'
0x1 1
0x1F 31
0x1FF 511
0x1FFF 8191
0x1FFFF 131071
0x1FFFFF 2097151
0x1FFFFFF 33554431
0x1FFFFFFF 536870911
0x1FFFFFFFF 8589934591
0x1FFFFFFFFF 137438953471
0x1FFFFFFFFFF 2199023255551
0x1FFFFFFFFFFF 35184372088831
0x1FFFFFFFFFFFF 562949953421311
0x1FFFFFFFFFFFFF 9007199254740991 <—- same 53-bit cutoff as everyone else
0x1FFFFFFFFFFFFFF 144115188075855872
0x1FFFFFFFFFFFFFFF 2305843009213693952
0x1FFFFFFFFFFFFFFFF 36893488147419103232
0x1FFFFFFFFFFFFFFFFF 590295810358705651712
至于gawk w/ GMP
,相同的语法也同样有效::
gawk -nMb '$++NF = +$_' gawk -nMb '$_+=_' # bare-minimum for decoding only
0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FF
4039625758913875912589359586083743995055512833714435504016293178
4405818923584863616496501764403641829610897451152372524367649448
9381136513688601904830603539007885967091262451146877471879870651
3349507204798008446034599027330327469520229761792309521308822705
7315045381303609469864426332260759440498904912981902392916737085
4280258562184832057118685702200441579025725972570741637827408855
7539268782324108022139590742950464807732169699793894037705738050
4622016541609039033907105888525262156446377158664154337098178225
8208724188074965854412482977694064579867966694295026692915370058
0664809825619018524194481701382449528831
0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FF
1363435169524269911828730305917228578060330202495290865539790982
7371516648113991190203302931565024840299466147451962815913873385
2522148007951372470634694349884863287355274123692326228153506025
6449621064516989547745964858178261655700151613192377357876272375
3156697498965000125995000331697960011316222573511904381270377443
4456738875664045570001855928254411693905730491114844096087034918
0110069521894617937431758347281426018625770132400320548130722516
6581169572950590530092280535153064143971968989561240329571796530
4034671009378453485380650812724095212618212396062157234960481467
1312496126201400644568812644451589364365644177632186208286933790
9829018821635355377770812129054951732940645330118952100067845292
9499538615211430772648425799413833175610717429354109678520925936
9213741889835945449962987646277279520936823276212321257829515103
3833942601963332189216012837778886594561378459489506510978807905
1381170571094365120915372807398095428422973784056054752315872220
6176340894514475543363534324174989951767803426763248896782575695
6047046953931595142033232854792107764474920475260981720944316709
8739461779304244458289383760901691933781958789000516340686979313
5446799567286170096351223188223641209884190975068669526951363494
8064661926449661593498469096732000102551481986625059721859097023
1726688811730072732699831526450745038544550742727648714254733590
7774351873409504567020373570218528016291856798481655030939274271
4787653513477621616294217160057179651190358795284704549316135872
5782302794393563446379952423431203733351023596207851972269134924
1473866429560425952173227274453753743028488443693826611022885537
5995608665402239484573975444565201963942537401503451945098413849
2184683355191045373463587431269003903982824275385711729868082132
6682795880292533526378698693688996358845682733278495368170564525
9456366887736325297172769244159702608462232371649579128861166308
3146179523134793830196042118066256429686413912354295908154824425
6130341819910904936906533980771755459818443263092971274710031970
7573512975965883846474277774713629817982323051193651451248156736
2996831962992996635761366324620420095941245617301824773213245469
3201871694784767029791611132886255360021651781010829953339166910
0115409479385913245176484047267776268252482696842335688711201114
8647555167444815602858442639151579849209445956292545371253991252
9247285581648184047805290035018987112075523528720001019297013315
3178377044945635942923139339040177840832567544772268363635082205
8618823457749648313208184464474111