我在 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;
我终于自己解决了这个问题。似乎以十六进制格式打印字节数据是非常基本的,并且 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'"