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

Hi,
I have a number in Decimal notation. I need to convert it to hexadecimal. For this, I am doing
#!/usr/bin/perl use strict; use warnings; my $decimal_number = "254"; my $hexadecimal_number = sprintf("%x",$decimal_number);

Once I convert decimal number to hexadecimal notation as above, I need to get the Most Significant Bit. That is, if I have a hexadecimal number such as "1110 1111", I need to extract the left most "1" bit and left shift the original hexadecimal number resulting in "1101 1110" as the new value.
Can any monks help me figure out how do I do this in Perl?
Note: Sorry, new to Perl. But so far liking what Perl lets me do for Bio-informatics :)
-Bella

Replies are listed 'Best First'.
Re: Decimal to Hexadecimal conversion and extraction MSB
by CountZero (Bishop) on Oct 17, 2009 at 06:58 UTC
    First a remark: 1110 1111 is not a hexadecimal number, it is in binary and it is not even the binary representation of 254, which is 11111110 (254 is even, so its rightmost bit cannot be 1).

    Taking that into account, the following will do what you want:

    use strict; my $debug = 1; my $dec= 254; print "$dec\n"; print shift_left($dec) ; sub dec2bin { my $str = unpack("B32", pack("N", shift)); $str =~ s/^0+(?=\d)//; # otherwise you'll get leading zeros print "$str\n" if $debug; return $str; } sub bin2dec { return unpack("N", pack("B32", substr("0" x 32 . shift, -32))); } sub shift_left { my $bin_reversed = reverse dec2bin(shift); chop $bin_reversed; print ' ' . reverse ($bin_reversed) . '0' . "\n" if $debug; return bin2dec(reverse ($bin_reversed) . '0'); }
    Output:
    254 11111110 11111100 252
    The bin2dec and dec2bin routines are from the Perl Cookbook (recipe 2.4).

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Decimal to Hexadecimal conversion and extraction MSB
by stbey (Novice) on Oct 17, 2009 at 07:54 UTC

    First of all, in Perl, your "$decimal_number" is internally stored in binary representation anyway.

    Your number is converted from the decimal literal "254" in your program text into binary when your program is compiled, and it is converted back to decimal every time you "print" the variable "$decimal_number" (actually, Perl caches the resulting string).

    Next, in two's complement binary representation, the MSB is the sign of the number. Since "254" is positive, you know that the MSB is 0. When a number is negative, you know that its MSB is 1.

    But this is probably not what you mean; from your example I deduce that you want the MSB of your decimal number in 8 bit representation.

    There are various solutions to this problem (as always in Perl, TIMTOWTDI ;-)). The simplest way is by using a mask, such as 0x80 (for 8 bits; for 16 bit numbers it would be 0x8000):

    $msb = $decimal_number & 0x80;

    Another solution (a bit of overkill here, though!) would go as follows:

    use Bit::Vector; $v = Bit::Vector->new(8); # 8 bits $v->from_Dec($decimal_number); $msb = $v->MSB();

    Hope this helps! :-)

Re: Decimal to Hexadecimal conversion and extraction MSB
by gmargo (Hermit) on Oct 17, 2009 at 06:46 UTC

    To find the most significant bit, you keep shifting right (with the ">>" operator), which is the same as an integer divide by two, until the value is zero. The last shifted bit is the most significant.

    But your question confuses me some - are you mixing "bit" with "hex digit"? If you left shift a hex "110 1111", you get "220 2222", not "1101 1110" (you need 4 left binary shifts, or 1 left hex digit shift). Are you extracting the most significant bit or the most significant hex digit? An example using digits other than 0 and 1 would help a lot if it's not a binary number.

Re: Decimal to Hexadecimal conversion and extraction MSB
by eye (Chaplain) on Oct 17, 2009 at 06:08 UTC
    ...I need to extract the left most "1" bit...

    I think it is easier to work from $decimal_number. To do this, you need to specify the size of the number. As an example, I'll use 16 bit integers. You can extract the most significant bit using the bit-and operator (&) and an appropriately chosen bit mask. The bit mask needs the high bit set and all others clear. For 16 bit integers, this would be 0x8000.

    my $msb = $decimal_number & 0x8000;

    $msb will be 0 or 2^15.

    ...left shift the original hexadecimal number...

    Again, start with $decimal_number. Use a bit mask to remove the most significant bit and then multiply the number by 2.

    my $new_number = $decimal_number & 0x7fff; $new_number *= 2;

    You can choose any intermediate result to translate to binary for display, but it is better to do this kind of manipulation on numbers rather than strings.

    Caveats: The integer size you are working with must not be larger than the integer size on your system. You need to understand how your system represents integers. Usually, the most significant bit is used for the sign in integers.

      my $msb = $decimal_number & 0x8000;
      extracts the leftmost bit of a 16 bit integer, not the leftmost '1' bit of any length integer, which is what was asked (I think).

      CountZero

      A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        I concur that the OP seems to be asking something different from what I answered. But the leftmost bit of a non-negative integer of any length is one unless the integer is zero. So I don't think that is what was intended.

        The OP could also have been asking for the position of the leftmost "1" bit. If the integer is not zero, this is the integer portion of the quotient "log(number) / log(2)" (for a non-zero base log).

        I read the tea leaves and inferred that the OP wanted to work with integers of a fixed size. (The part of the question that asked about bit shifting gave me a strong push in this direction. That is surely a prejudice based on my experience.)

Re: Decimal to Hexadecimal conversion and extraction MSB
by Marshall (Canon) on Oct 17, 2009 at 12:05 UTC
    Perl has the same type of bitwise shift operators that 'C' does.

    There is no need to fiddle around with some intermediate hex form, just operate on the int like you would in 'C'. My MSbit() below returns power of 2 bit position for the int that was supplied. The left shift and right shift operators work just like you would expect in C.

    Use the printf ("%x") or ("%X") descriptor as you wish to print the values.

    #!/usr/bin/perl -w use strict; print "power of 2 bit of 1 is: ",MSbit(1),"\n"; #prints 0 print "power of 2 bit of 8 is: ",MSbit(8),"\n"; #prints 3 my $num = 256; print "power of 2 bit of $num is: ",MSbit($num),"\n"; #prints 8 $num = $num << 1; print "power of 2 bit of $num is: ",MSbit($num),"\n"; #prints 9 $num = 254; print "power of 2 bit of $num is: ",MSbit($num),"\n"; #prints 7 $num = $num << 1; print "power of 2 bit of $num is: ",MSbit($num),"\n"; #prints 8 #Most Significant bit numbering is like #...9876543210 # sub MSbit ##returns power of 2 of MS bit { my $num = shift; die "illegal value in MSbit" if $num == 0; my $bit =-1; while ($num != 0) { $num = $num >> 1; $bit++; } return $bit; } __END__ output of above is: power of 2 bit of 1 is: 0 power of 2 bit of 8 is: 3 power of 2 bit of 256 is: 8 power of 2 bit of 512 is: 9 power of 2 bit of 254 is: 7 power of 2 bit of 508 is: 8
    Update:

    yes, just like $n = $n +2; can be replaced by $n +=2;, $num = $num >> 1 can be replaced by $num >>= 1; this is also fine. I would also add that if you have a 128 bit, 64 bit, 32 bit, 16 bit machine the above algorithm will work.

Re: Decimal to Hexadecimal conversion and extraction MSB
by GrandFather (Saint) on Oct 17, 2009 at 08:02 UTC

    You've a few answers to your immediate problem as expressed, and some hints at areas where the information you gave was a little fuzzy. However, depending on what you are actually trying to achieve, there may be some rather subtle problems with what you are trying to do. Can you tell us about the bigger picture so we can give an answer that addresses the specific problem you have rather than various problems you may have and that we may guess at?


    True laziness is hard work
Re: Decimal to Hexadecimal conversion and extraction MSB
by Marshall (Canon) on Oct 17, 2009 at 16:22 UTC
    Oh, I see now that what you want to implement is a virtual register with an arbitrary number of bits. The width of the register is defined by the left most bit of the input number. Then you want to do a left shift by one bit.

    That is quite frankly a VERY strange requirement and I missed this strange specification on my first attempt. Your spec:

    C 7654 3210 1110 1111 = 0xEF = decimal 239 1 1101 1110 left shift one showing carry 1101 1110 result without carry = 0xDE = 222
    But this is possible! Recyling my MSbit() code I get your desired output of 222 from your input of 239.

    BUT there has to be something wrong in terms of the specification! Basically as you have seen, there are many ways to implement bit operations in Perl. What you are asking for is so very strange, I can't believe that it is right. Please reformulate your question with a very clear set of input and output values. If you are dealing with 8 bit values, then the code becomes a lot easier!

    #!/usr/bin/perl -w use strict; my $input = 0xEF; my $andMask = (2**(MSbit($input)+1))-1; printf ("%X\n", ($input<<1) & $andMask); ## prints DE sub MSbit ##returns power of 2 of MS bit { my $num = shift; my $bit =-1; while ($num != 0) { $num = $num >> 1; $bit++; } return $bit; }
    If you just want a virtual 8 bit register:

    #!/usr/bin/perl -w use strict; my $input = 0xEF; #this is 239 in decimal my $mask = 0xFF; printf ("%X\n", $input<<1 & $mask); # prints DE which means 222 in decimal
    If you mean just an 8 bit virtual register, here is the one line version:
    printf ("%X\n", 0xEF<<1 & 0xFF); # prints DE which means 222 in decimal
    And yes, printf ("%X\n", 239<<1 & 0xFF); #will do the same thing!
Re: Decimal to Hexadecimal conversion and extraction MSB
by lamprecht (Friar) on Oct 17, 2009 at 09:26 UTC
    ... if I have a hexadecimal number such as "1110 1111", I need to extract the left most "1" bit and left shift the original hexadecimal number resulting in "1101 1110" as the new value. ...

    here is one more:

    perl -e"$n = shift; for (1..9){ print substr (unpack('B32', pack('N', $n)), 24,8); $n=($n*2)%512; print ' - shifting: ' ,$n>255? 1:0,qq/\n/; }" 239 11101111 - shifting: 1 11011110 - shifting: 1 10111100 - shifting: 1 01111000 - shifting: 0 11110000 - shifting: 1 11100000 - shifting: 1 11000000 - shifting: 1 10000000 - shifting: 1 00000000 - shifting: 0

    Cheers, Christoph
Re: Decimal to Hexadecimal conversion and extraction MSB
by Anonymous Monk on Oct 17, 2009 at 17:01 UTC
    Thanks to all who helped me with your suggestions. I took all the helpful suggestions and implemented just what we needed :) -Bella