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

Dear monks

I've lost count of how many times I needed a conversion from a binary string ("1101...") to a signed integer. Each time I try to think of an elegant solution and end up with brute-force math (flip bits, add 1 to compute 2s complement, then use bin2dec). The binary->unsigned conversion is (from the Cookbook):

sub bin2dec { return unpack("N", pack("B32", substr("0" x 32 . shift, -32))); }

So, is there any way to apply pack/unpack magic to convert a binary string (of any length) to a signed, 2s complement integer ?

UPDATE:

Here is one solution

# Given a lsb-first bitstring, returns # a 2s complement signed integer # sub bin2signed { my $bitstr = $_[0]; my $is_negative = 0; if (substr($bitstr, -1, 1) eq "1") { $bitstr =~ tr/10/01/; $is_negative = 1; } my $result = 0; for (my $i = 0; $i < length($bitstr); ++$i) { $result += substr($bitstr, $i, 1) * (2 ** $i); } if ($is_negative) { $result += 1; $result *= -1; } return $result; }

Replies are listed 'Best First'.
Re: signed bin2dec
by BrowserUk (Patriarch) on Mar 19, 2004 at 10:25 UTC
    sub sb2d{ my $b = unpack 'N', pack("B32", substr("0" x 32 . shift, -32)); $b > 2**31 ? -(1+~$b) : $b }

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      This surely works, but unfortunately limited to 32 bit strings.

        UPDATE:Just in case there is anyone (else?) who hasn't spotted the joke.

        Variable-length, signed binary strings DO NOT MAKE ANY SENSE!!!.

        What values do the following binary strings represent?

        • 0b10

          2 or -0?

        • 0b11111111111111111111111111111111111111111111

          A big integer or -1?

        You cannot two's complement a variable number of bits without fixing the length. Yes. This post was sacarstic and should have been identified as such, but I thought it obvious.

        END UPDATE

        Okay. Try this version. It will handle up to 1023 digit binary strings. Beyond that, you'll probably need Math::BigInt

        sub sb2d{ my $n=shift; return undef unless $n =~ m[^[01]+$]; my $s=ord( $n )==49; $n=~tr[01][10] if $s; $n = eval{ no warnings; eval '0b'.$n;}; $s ? -1 * ++$n : $n }

        Update: The above is overly crude, so here is (I think) a slightly improved version. It still uses eval and requires no warnings;, but this seems to me acceptable to re-use perl's built-in binary string parser rather than role your own? It should be relatively safe unless anyone can see a way of injecting "nasty code" that uses only '0's & '1's?

        sub signedBin2Dec{ my $n=shift; return undef unless $n =~ m[^([01])[01]+$]; $n=~tr[01][10] if $1; $n = do{ no warnings; eval '0b'. $n}; $1 ? -1 * ++$n : $n }

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail