pack/unpack help please

Discussion in 'Perl Misc' started by Dave Saville, Apr 5, 2014.

  1. Dave Saville

    Dave Saville Guest

    I am using perl on a Rasberry pi to read an i2c device using the hipi
    library.

    @temp = $dev->bus_read( MPU6050_TEMP_OUT_H, 2 );

    Returns two bytes as an array of two values. High byte, low byte.

    For example I may get:

    $temp[0] = 244; # 0xFD
    $temp[1] = 224; # 0xE0

    Which is -2848. I (eventually) came up with this

    $temp = unpack 's', pack 'CC', reverse @temp;

    Is there a better method? Also why does

    $temp = unpack 'n', pack 'CC', @temp;

    Not give the same value?

    TIA
     
    Dave Saville, Apr 5, 2014
    #1
    1. Advertisements

  2. Dave Saville

    Dave Saville Guest

    Ah, n is unsigned.

    $temp = unpack 's>', pack 'CC', @temp;

    Does the trick.
     
    Dave Saville, Apr 5, 2014
    #2
    1. Advertisements

  3. This is 0xf4, not 0xfd.
    What's you definition of 'better' here? You can generally build the
    number by 'putting the bytes in the right place', ie

    $temp[0] << 8 | $temp[1]

    If you want it to be interpreted as signed, two ways to achieve that
    would be

    ($temp[0] << 8 | $temp[1]) - ($temp[0] & 0x80 && 0x1000)

    and

    use integer;
    ($temp[0] << 8 | $temp[1]) ^ ($temp[0] & 0x80 && HIBITS)

    with HIBITS defined as

    use constant HIBITS => -1 & ~0xffff;

    both are faster (for me) than your approach, OTOH, you'll very likely
    freak out people to whom it never occured that computers actually work
    with binary numbers and that the bit operators are useful.

    sample code:
    -------------
    use Benchmark;

    use constant HIBITS => -1 & ~0xffff;

    @t = (0xf4, 0xe0);

    timethese(-3,
    {
    pack => sub {
    return unpack('s', pack('CC', reverse(@t)));
    },

    calc => sub {
    use integer;
    return ($t[0] << 8 | $t[1]) ^ ($t[0] & 0x80 && HIBITS);
    }});
     
    Rainer Weikusat, Apr 5, 2014
    #3
  4. The xor is somewhat out-of-place here because there are no overlapping
    1-bits in both numbers. | can be used as well.
     
    Rainer Weikusat, Apr 5, 2014
    #4
  5. Dave Saville

    Dave Saville Guest

    Yup - typo and/or brain to keyboard. :)
    That's what I thought at first, but the sign stumped me.
    Hmm I see how the second one works but the first doesn't :

    use strict;
    use warnings;

    my @temp;
    $temp[0] = 244; # 0xF4
    $temp[1] = 224; # 0xE0
    printf "%X %X\n", @temp;
    printf "%X\n", $temp[0] & 0x80;
    printf "%X\n", $temp[0] & 0x80 && 0x1000;
    printf "%X\n", ($temp[0] << 8 | $temp[1]) - ($temp[0] & 0x80 &&
    0x1000);
    printf "%d\n", ($temp[0] << 8 | $temp[1]) - ($temp[0] & 0x80 &&
    0x1000);
    use integer;
    use constant HIBITS => -1 & ~0xffff;
    printf "%X\n", HIBITS;
    printf "%X\n", $temp[0] & 0x80;
    printf "%X\n", $temp[0] & 0x80 && HIBITS;
    printf "%X\n", ($temp[0] << 8 | $temp[1]) ^ ($temp[0] & 0x80 &&
    HIBITS);
    printf "%d\n", ($temp[0] << 8 | $temp[1]) ^ ($temp[0] & 0x80 &&
    HIBITS);

    [T:\tmp]try.pl
    F4 E0
    80
    1000
    E4E0
    58592
    FFFF0000
    80
    FFFF0000
    FFFFF4E0
    -2848
     
    Dave Saville, Apr 6, 2014
    #5
  6. My bad. The final number in the second term should be 0x10000 (65536),
    not 0x1000 (4096).

    perl -e 'print((0xf4 << 8 | 0xe0) - (0xf4 & 0x80 && 0x10000))'

    Both rely on negative integers being represented as two's complement. In
    this case, a negative n-bit integer is encoded as 'distance' from 2**n,
    ie 0xffff aka 65535 is -1.
     
    Rainer Weikusat, Apr 6, 2014
    #6
  7. Dave Saville

    Dave Saville Guest

    What's a factor of 16 between friends? :)
    I am happy playing with bits - I just could not see what you were
    trying to do there. Whereas your second method was obvious what it
    did.

    Just for fun I added another I dreamed up to your benchmark

    kalk => sub {
    my $x = $t[0] << 8 | $t[1];
    return $t[0] & 0x80 ? ~(~$x & 0xffff) : $x;

    Which sits between the two.

    Thanks.
     
    Dave Saville, Apr 6, 2014
    #7
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.