thanos1983 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

Update: Title update from (How to convert an integer 8bit long to binary and vice versa) to (pack and unpack with 8 bit integers).

Lately I have been asking way too many questions, apologies for that. Although that I thought that I was starting to get the conversion process of decimal to binary and binary to decimal, I got stack again.

I want to convert an integer 8 bit long with template format "i" to binary and then convert it pack to decimal with the same format "i". I want to complete the process using pack and unpack.

I have been experimenting with a sample of code. I can not figure it out why is not working properly.

Sample of code:

#!/usr/bin/perl use strict; use warnings; sub dec2bin { my $bits = shift; my $size = shift; my $template = shift; my $str = unpack("B$size", pack($template, $bits)); return $str; } sub bin2dec { my $bits = shift; my $size = shift; my $template = shift; my $substr = substr("0" x $size . $bits , -$size); print "Substring: ".$substr."\n"; my $pack = pack("B$size", $substr); print "Pack: ".$pack."\n"; my $unpack = unpack($template, $pack); return $unpack; } my $int2bin = dec2bin( 3 , 8 , "i" ); print "Int2bin: ".$int2bin."\n"; my $binary2int = bin2dec( $int2bin , 8 , "i" ); print "Decimal from binary: ".$binary2int."\n"; __END__ Int2bin: 00000011 Substring: 00000011 Pack:  Use of uninitialized value $binary2int in concatenation (.) or string +at test.pl line 32. Decimal from binary:

I have broken down the process into pieces in order to detect the error. It seems the error comes from the pack process.

So at this point I can not understand why. I have found a way to by avoid the error by modifying the $template on the sub bin2dec process from "i" (intiger) to "c" (8 bit character). By doing so I get the correct output, but since I am defining B8 I should not have a problem with the unpack process.

So can someone help me understand where I am going wrong, and how to fix my problem?

Thanks in advance for everyone's time and effort to assist me.

Seeking for Perl wisdom...on the process of learning...not there...yet!

Replies are listed 'Best First'.
Re: pack and unpack with 8 bit integers
by Anonymous Monk on Sep 22, 2014 at 12:13 UTC

    The problem is that the i template needs at least 32 bits, but you're only giving it 8. The $size argument to bin2dec should be 32, but even then your function won't always work depending on the endianness of your system!

    $ perl -wMstrict -le 'print unpack("B*",pack("i",3))' 00000011000000000000000000000000 $ perl -wMstrict -le 'print unpack("i",pack("B*", "00000011000000000000000000000000"))' 3 $ perl -wMstrict -le 'print unpack("i",pack("B*", "00000000000000000000000000000011"))' 50331648 $ perl -wMstrict -le 'print unpack("i",pack("B*","00000011")).""' Use of uninitialized value in concatenation (.) or string at -e line 1 +. $ perl -wMstrict -le 'print unpack("c",pack("B*","00000011"))' 3

    (Note the use of "B*" instead of "B$size".) Some more general observations:

    The i template is system-dependent. Unless the binary data itself is system-dependent, such as from C structs, I'd only use the system-independent formats, in this case for example the cCnNvV formats since those have a fixed size and endianness.

    You say want to use i, but then only want handle 8 bits, that does not go together - why not use c to begin with? Also, you're not checking that your values will actually fit in 8 bits!

    Lastly, I suspect you've got an XY Problem and you might want to explain to us what you're trying to accomplish overall. Don't worry about asking too many questions, but I would suggest you play around with pack and unpack a bit more, like I did above!

      Hello Anonymous Monk,

      I was afraid that the int size of "i" would be 32 bits, because I have been playing around and I could get the correct output with 32

      perl -wMstrict -le 'print unpack("i",pack("B32","00000011"))' 3

      I was hopping since I get no error when I am using pack I would not have any problems when I will use unpack.

      perl -wMstrict -le 'print unpack("B8",pack("i",3))' 00000011

      I noticed the: (Note the use of "B*" instead of "B$size".)

      Ok then let me describe from the beginning what I am trying to achieve.

      I want to convert decimal numbers on three-bit integer (signed and unsigned). So far I have managed to achieve it with the following piece of code:

      #!/usr/bin/perl use strict; use warnings; sub dec2bin { my $bits = shift; my $size = shift; my $template = shift; return unpack("B$size", pack($template, $bits)); } sub bin2dec { my $bits = shift; my $size = shift; my $template = shift; return unpack($template, pack("B$size",substr("0" x $size . $bits +, -$size))); } my $int2bin = dec2bin( 3 , 8 , "c" ); print "Int2bin: ".$int2bin."\n"; my $binary2int = bin2dec( $int2bin , 8 , "c" ); print "Decimal from binary: ".$binary2int."\n";

      My hesitation and the reason that I created the question was that I read on pack i A signed integer value. , I A unsigned integer value. (This 'integer' is _at_least_ 32 bits wide. Its exact size depends on what a local C compiler calls 'int'.) , c A signed char (8-bit) value. and finally C An unsigned char (octet) value.. This is the reason that I was trying to find a solution by using "i" and "I".

      At this point I do not know if I should use "c" or "C" as an alternative to int and unsigned int for my task.

      Thank you for your time and effort, reading and replying to my question.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
        I want to convert decimal numbers on three-bit integer (signed and unsigned).

        Ok, but why? That's what is meant by an XY Problem - are you doing this for example because you're communicating with some other software that requires data in this format (if so, what is that format? perhaps show some sample data?), just for practice / fun, etc.? It can help to know what the broader picture is. In this case the main reason being that Perl's pack and unpack are not always the right tool for the job (also there are some pack templates that I'd stay away from as they can get too complex). So far, I've only found them useful for decoding C structs, handling binary data in network protocols, or for debugging.

        Anyway, to get to your questions:

        ... This is the reason that I was trying to find a solution by using "i" and "I".

        I find the best way to think about pack templates is C data types. Think of i as int, and that'll make it clear that i is (unfortunately) not "as many bits as I want".

        At this point I do not know if I should use "c" or "C" as an alternative to int and unsigned int for my task.

        As said before, unless you know that you want the system-dependent formats, stick with the system-independent formats cCnNvV, perhaps with the ! modifier to make them signed, or sl with one of the <> modifiers to force endianness. Then pick according to required size, signedness, and endianness.

        I want to convert decimal numbers on three-bit integer (signed and unsigned).

        A three-bit signed integer? I don't think Perl has built-in support for that, so you'll probably need to build your own Sign extension, as some quick searching has not (yet) shown me any CPAN modules. If you want to do it on binary values, see perlop for the bitwise ops, also maybe vec; or you could do it on the string representation (which will probably be a little slower). Or, with just 8 possible values, a look-up table may be the easiest way!

Re: How to convert an integer 8bit long to binary and vice versa
by parv (Parson) on Sep 22, 2014 at 11:13 UTC

      Hello parv,

      Thank you for your time and effort, I assumed the title will not produce confusion, but you are right. I hope the new title produces less confusion and describes more correctly the question.

      Seeking for Perl wisdom...on the process of learning...not there...yet!