Difference between passing string "0x30" and hexadecimal number 0x30 to hex() function - perl

print hex("0x30"); gives the correct hex to decimal conversion.
What does
print hex(0x30); mean?
The value it's giving is 72.

hex() takes a string argument, so due to Perl's weak typing it will read the argument as a string whatever you pass it.
The former is passing 0x30 as a string, which hex() then directly converts to decimal.
The latter is a hex number 0x30, which is 48 in decimal, is passed to hex() which is then interpreted as hex again and converted to decimal number 72. Think of it as doing hex(hex("0x30")).
You should stick with hex("0x30").
$ perl -e 'print 0x30';
48
$ perl -e 'print hex(0x30)';
72
$ perl -e 'print hex(30)';
48
$ perl -e 'print hex("0x30")';
48
$ perl -e 'print hex(hex(30))';
72

To expand on marcog's answer: From perldoc -f hex
hex EXPR: Interprets EXPR as a hex string and returns the corresponding value.
So hex really is for conversion between string and hex value. By typing in 0x30 you have already created a hex value.
perl -E '
say 0x30;
say hex("0x30");
say 0x48;
say hex(0x30);
say hex(hex("0x30"));'
gives
48
48
72
72
72

hex() parses a hex string and returns the appropriate integer.
So when you do hex(0x30) your numeric literal (0x30) gets interpreted as such (0x30 is 48 in hex format), then hex() treats that scalar value as a string ("48") and converts it into a number, assuming the string is in hex format. 0x48 == 72, which is where the 72 is coming from.

Related

Perl - convert hexadecimal to binary and use it as string

I am new to Perl and I have difficulties using the different types.
I am trying to get an hexadecimal register, transform it to binary, use it a string and get substrings from the binary string.
I have done a few searches and what I tried is :
my $hex = 0xFA1F;
print "$hex\n";
result was "64031" . First surprise : can't I print the hex value in Perl and not just the decimal value ?
$hex = hex($hex);
print "$hex\n";
Result was 409649. Second surprise : I would expect the result to be also 64031 since "hex" converts hexadecimal to decimal.
my $bin = printf("%b", $hex);
It prints the binary value. Is there a way to transform the hex to bin without printing it ?
Thanks,
SLP
Decimal, binary, and hexadecimal are all text representations of a number (i.e. ways of writing a number). Computers can't deal with these as numbers.
my $num = 0xFA1F; stores the specified number (sixty-four thousand and thirty-one) into $num. It's stored in a format the hardware understands, but that's not very important. What's important is that it's stored as a number, not text.
When print is asked to print a number, it prints it out in decimal (or scientific notation if large/small enough). It has no idea how the number of created (from a hex constant? from addition? etc), so it can't determine how to output the number based on that.
To print an number as hex, you can use
my $hex = 'FA1F'; # $hex contains the hex representation of the number.
print $hex; # Prints the hex representation of the number.
or
my $num = 0xFA1F; # $num contains the number.
printf "%X", $num; # Prints the hex representation of the number.
You are assigning a integer value using hexadecimal format. print by default prints numbers in decimal format, so you are getting 64031.
You can verify this using the printf() by giving different formats.
$ perl -e ' my $num = 0xFA1F; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$ perl -e ' my $num = 64031; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$ perl -e ' my $num = 0b1111101000011111; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$
To get the binary format of 0xFA1F in string, you can use sprintf()
$ perl -e ' my $hex = 0xFA1F; my $bin=sprintf("%b",$hex) ; print "$bin\n" '
1111101000011111
$
lets take each bit of confusion in order
my $hex = 0xFA1F;
This stores a hex constant in $hex, but Perl doesn't have a hex data type so although you can write hex constants, and binary and octal constants for that matter, Perl converts them all to decimal. Note that there is a big difference between
my $hex = 0xFA1F;
and
my $hex = '0xFA1F';
The first stores a number into $hex, which when you print it out you get a decimal number, the second stores a string which when printed out will give 0xFAF1 but can be passed to the hex() function to be converted to decimal.
$hex = hex($hex);
The hex function converts a string as if it was a hex number and returns the decimal value and, as up to this point, $hex has only ever been used as a number Perl will first stringify $hex then pass the string to the hex() function to convert that value from hex to decimal.
So to the solution. You are almost there with printf(),there is a function called sprintf() which takes the same parameters as printf() but instead of printing the formatted value returns it as a string. So what you need is.
my $hex = 0xFA1F;
my $bin = sprintf("%b", $hex);
print $bin;
Technical note:
Yes I know that Perl stores all its numbers internally as binary, but lets not go there for this answer, OK?
If you're ok with using a distribution, I wrote Bit::Manip to make my prototyping a bit easier when dealing with registers (There's also a Pure Perl version available if you have problems compiling the XS code).
Not only can it fetch out bits from a number, it can toggle, clear, set etc:
use warnings;
use strict;
use Bit::Manip qw(:all);
my $register = 0xFA1F;
# fetch the bits from register using msb, lsb
my $msbyte = bit_get($register, 15, 8);
print "value: $msbyte\n";
print "bin: " . bit_bin($msbyte) . "\n";
# or simply:
# printf "bin: %b\n", $msbyte;
Output:
value: 250
bin: 11111010
Here's a blog post I wrote that shows how to use some of the software's functionality with an example datasheet register.

bug in perl with prefixed 0 numbers

when I try to execute the following(01434.210 instead of 1434.210)
$val=22749.220-(21315.010+01434.210)
print $val
I get these output
output 638.207900000001
But according to me output must be 0.
What am I missing?
A leading 0 in a literal number makes Perl interpret the value I'm base 8:
123 # 123, in decimal
0123 # 123 in octal, but 83 in decimal
This isn't the same for strings converted to numbers. In those Perl ignores the leading 0s. The string-to-number conversion only deals in base-10:
"123" + 0 # 123
"0123" + 0 # still 123
In your example in the comment, you convert a literal number to a string with a leading zero. When you convert that string back to its numeric form you get the same value you started with:
$val=sprintf("%05d",1434); # converting 1434 to the string "01434"
print $val; print "\n"; # still a string
print $val+21315; # "01434" + 21315 => 1434 + 21315
print "\n";
print 01434+21315; # oct(1434) + 21315 => 796 + 21315
The leading zero notation helps with certain builtins that typically use octal numbers, such as those that deal with unix permissions:
chmod 0644, #files

how to print the result from pack function?

I like to verify what pack does. I have the following code to give it a try.
$bits = pack 'N','134744072';
how to print bits ?
I did the following:
printf ("bits = %032b \n", $bits);
but it does not work.
Thanks !!
If you want the binary representation of a number, use
my $num = 134744072;
printf("bits = %032b\n", $num);
If you want the binary representation of a string of bytes, use
my $bytes = pack('N', 134744072);
printf("bits = %s\n", unpack('B*', $bytes));
The Devel::Peek module (which comes with Perl) allows you to examine Perl's representation of the variable. This is probably more useful than just a raw print when you're dealing with binary data rather than printable character strings.
#!/usr/bin/perl
use strict;
use warnings;
use Devel::Peek qw(Dump);
my $bits = pack 'N','134744072';
Dump($bits);
Which produces output like this:
SV = PV(0xaedb20) at 0xb15650
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0xb06630 "\10\10\10\10"\0
CUR = 4
LEN = 10
The 'SV' at the beginning indicates that this is a dump of a 'scalar value' (as opposed to say an array or a hash value).
The 'SV = PV' indicates that this scalar contains a string of bytes (as opposed to say an integer or floating point value).
The 'PV = 0xb06630' is the pointer to where those bytes are located.
The "\10\10\10\10"\0 is probably the bit you're interested in. The double quoted string represents the bytes making up the contents of this string.
Inside the string, you would typically see the bytes interpreted as if they were ASCII, so the byte 65 decimal would appear as 'A'. All non-printable characters are displayed in octal with a preceding \.
So your $bits variable contains 4 bytes, each octal '10' which is hex 0x08.
The LEN and CUR are telling you that Perl allocated 10 bytes of storage and is currently using 4 of them (so length($bits) would return 4).

Perl: Encoding binary number to base64

I have a string that is essentially binary
my $string = "000110";
I've been trying to use encode_base64 but that encodes strings, if im reading the documentation correctly.
my $j = MIME::Base64->encode_base64($string);
print "$j\n"; # should print 'A'
>> TUlNRTo6QmFzZTY000000
How can I achive this in perl? the string is expected to be ~120 binary bits in length.
I'd rather not use any modules that are not installed with perl by default, the target audience for this script is not familiar with the shell.
Edit:
A lot of the answers to this question have been surrounded about strings, not actual numbers, there was one solution I found, but it required Math::BaseCalc module to be installed.
Edit2: Essentially, if i have
my $binary_string = "000110";
i would like to have it encoded in base64 (as a number), so it returns
>>G # for this case (binary number 000110 to base64 number = G)
base64 is an algorithm that converts strings of 8-bit bytes/characters. Anything else must be packed into bytes.
You already have a string, but you could be more space-efficient by packing the 120 bits into 15 bytes using the following:
my $base64 = encode_base64(pack("B*", $binary), "");
The inverse operation is
my $binary = unpack("B*", decode_base64($base64));
For example,
$ perl -MMIME::Base64 -E'say encode_base64(pack("B*", $ARGV[0]), "")' \
0100000101000010
QUI=
$ perl -MMIME::Base64 -E'say unpack("B*", decode_base64($ARGV[0]))' \
QUI=
0100000101000010
If you have actually have a number of bits that's not divisible by 8, you can prefix the string with the number of bits.
my $base64 = encode_base64(pack("CB*", length($binary), $binary), "");
The inverse operation is
my ($length, $binary) = unpack("CB*", decode_base64($base64));
substr($binary, $length) = "";

Handle command line arguments with different radices in Perl

When used as literals in a Perl program, numbers are handled in the same way as in C: "0x" prefix means hexadecimal, "0" prefix means octal, and no prefix means decimal:
$ perl -E 'print 0x23 . "\n"'
35
$ perl -E 'print 023 . "\n"'
19
$ perl -E 'print 23 . "\n"'
23
I would like to pass command line arguments to Perl using the same notation. e.g. If I pass 23, I want to convert the string argument to a decimal value (23). If I pass 0x23, I want to convert to a hexadecimal value (35), and 023 would be converted to octal (19). Is there a built-in way to handle this? I am aware of hex() and oct(), but they interpret numbers with no prefix to be hex/oct respectively (not decimal). Following this convention, it seems that I want a dec() function, but I don't think that exists.
From http://answers.oreilly.com/topic/419-convert-binary-octal-and-hexidecimal-numbers-in-perl/:
print "Gimme an integer in decimal, binary, octal, or hex: ";
$num = <STDIN>;
chomp $num;
exit unless defined $num;
$num = oct($num) if $num =~ /^0/; # catches 077 0b10 0x20
printf "%d %#x %#o %#bn", ($num) x 4;