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

问题描述

我在 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)

解决方法

嗯,%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;