I am using sprintf
in Perl to convert decimal numbers to their hex equivalents, as in:
my $positive = sprintf("X'%02X'", 1); # X'01'
my $negative = sprintf("X'%02X'", -1); # X'FFFFFFFFFFFFFFFF'
Why does the negative number always get sign-extended to the 8-byte representation of the number, even if the precision specified in sprintf
is smaller?
Is there a way to avoid this so I don't have to substring the value every time? The same result occurs even if I remove the 0
, as in: sprintf("X'%2X'", -1)
.
Well, %x
specifically takes an unsigned integer, so when you give it the bit pattern for a signed integer, it treats all of those bits as an unsigned one. The length of the output probably depends on how big your compiler made numbers (although I'm guessing most modern things are the same now). Perl stores most of its numbers as doubles, so that's the size of the number. It's one of few places where Perl exposes the underlying architecture.
This means that you need to handle the sign yourself and use the absolute value of the number:
sprintf( "%s%2x", ($n < 0 ? '-' : ''), abs($n) );
As for the sprintf field specifiers, you are not specifying a precision there. That's minimum width. The %f
and %g
specifiers may have a maximum number of decimal places, but the entire string can still overflow.
But, it turns out that your problem is different. You want the values from -128 to 127 it their signed integer form. So, -1 gets FF
. You need to add a little bit to get that.
my $s = sprintf( "%2x", $n );
$s = substr( $s, -2 ) if $n < 0;