Perl 十六进制数的负表示总是符号扩展为 8 个字节

问题描述 投票:0回答:2

我在 Perl 中使用

sprintf
将十进制数转换为其等值的十六进制数,如下所示:

my $positive = sprintf("X'%02X'", 1);   # X'01'
my $negative = sprintf("X'%02X'", -1);  # X'FFFFFFFFFFFFFFFF'

为什么负数总是被符号扩展为数字的 8 字节表示形式,即使

sprintf
中指定的精度更小?

有没有办法避免这种情况,这样我就不必每次都对值进行子字符串化?即使我删除

0
,也会出现相同的结果,如:
sprintf("X'%2X'", -1)

perl hex printf
2个回答
2
投票

好吧,

%x
专门采用无符号整数,因此当您为其提供有符号整数的位模式时,它会将所有这些位视为无符号位。输出的长度可能取决于编译器产生的数字有多大(尽管我猜现在大多数现代的东西都是一样的)。 Perl 将大部分数字存储为双精度数,因此这就是数字的大小。这是 Perl 公开底层架构的少数几个地方之一。

这意味着你需要自己处理符号并使用数字的绝对值:

sprintf(  "%s%2x", ($n < 0 ? '-' : ''), abs($n) );

至于 sprintf 字段说明符,您没有在那里指定精度。这是最小宽度。

%f
%g
说明符可能具有最大小数位数,但整个字符串仍然可能溢出。

但是,事实证明你的问题有所不同。您需要 -128 到 127 之间的值作为有符号整数形式。所以,-1 得到

FF
。您需要添加一点才能得到它。

my $s = sprintf( "%2x", $n );
$s = substr( $s, -2 ) if $n < 0;

0
投票

我终于自己解决了这个问题。似乎以十六进制格式打印字节数据是非常基本的,并且 sprintf 应该很好地支持这一点,但即使是“C”sprintf 在处理无符号值时也存在问题,因为它需要一个有符号的 int。 Perl 的情况类似(如 @brian-d-foy 答案中所述):

printf("%.2hhx", $byte);

printf("%02hhx", $byte);

将根据需要仅打印 2 个十六进制值。

这与 sprintf 的“C”实现不同,并且 Perl sprintf 文档的某些副本中似乎缺少“hh”,因此可能相对较新。

尺寸

For numeric conversions, you can specify the size to interpret the number as using l, h, V, q, L, or ll

https://perldoc.perl.org/functions/sprintf

在 OP 案例中:

sprintf("X'%02hhX'", -1) eq "X'FF'"
© www.soinside.com 2019 - 2024. All rights reserved.