Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

reliable get position of leftmost bit in large integers

by wollmers (Scribe)
on Mar 12, 2015 at 10:23 UTC ( [id://1119766]=perlquestion: print w/replies, xml ) Need Help??

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

Hi monks,

In an algorithm I need to test, if the positions of leftmost bits of two bitvectors are equal:

if ( int(log($v1)/log(2)) == int(log($v2)/log(2)) ) { #match }

But this is unreliable because of the float-precision :

$ perl -e 'for my $i (1..63) {if(int(log(2**$i-1)/log(2)) != $i-1) {pr +int "$i\n"}}' 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

Is there a reliable solution with comparable speed? A split into two 32-bit vectors would be an ugly workaround. Or should I redesign this algorithm LCS_64i() at a higher level?

TIA

Helmut Wollmersdorfer

Replies are listed 'Best First'.
Re: reliable get position of leftmost bit in large integers
by martin (Friar) on Mar 12, 2015 at 13:39 UTC
    To check whether the most significant bit positions of two unsigned integer values are equal (or both are zero), this works for me:
    sub msb_pos_is_equal { my ($x, $y) = @_; ($x ^ $y) <= ($x & $y) }
    Update: Added the words "unsigned integer" in the first paragraph.

      Thanks. This smells very fast. I still need the value of the position, but only in case of a match, and only from one side. Then BrowserUK's solution should do it.

      WOW, maybe we can beat the speed of Algorithm::Diff::XS in pure Perl. Still reached 80% yesterday.

      Helmut Wollmersdorfer

      Nice! ++

Re: reliable get position of leftmost bit in large integers
by BrowserUk (Patriarch) on Mar 12, 2015 at 12:01 UTC

    This is very efficient in C. It works in Perl, but you'll have to time it and compare it against the other methods:

    #! perl -slw use strict; sub nextPowerOfTwo { use integer; my $v = shift; $v--; $v |= $v >> 1; $v |= $v >> 2; $v |= $v >> 4; $v |= $v >> 8; $v |= $v >> 16; return ++$v; } print "$_ : ", nextPowerOfTwo( $_- 1 ) for map 1 << $_, 0 .. 63; __END__ C:\test>nextPowerOfTwo.pl 1 : 0 2 : 1 4 : 4 8 : 8 16 : 16 32 : 32 64 : 64 128 : 128 256 : 256 512 : 512 1024 : 1024 2048 : 2048 4096 : 4096 8192 : 8192 16384 : 16384 32768 : 32768 65536 : 65536 131072 : 131072 262144 : 262144 524288 : 524288 1048576 : 1048576 2097152 : 2097152 4194304 : 4194304 8388608 : 8388608 16777216 : 16777216 33554432 : 33554432 67108864 : 67108864 134217728 : 134217728 268435456 : 268435456 536870912 : 536870912 1073741824 : 1073741824 2147483648 : 2147483648 4294967296 : 4294967296 8589934592 : 8589934592 17179869184 : 17179869184 34359738368 : 34359738368 68719476736 : 68719476736 137438953472 : 137438953472 274877906944 : 274877906944 549755813888 : 549755813888 1099511627776 : 1099511627776 2199023255552 : 2199023255552 4398046511104 : 4398046511104 8796093022208 : 8796093022208 17592186044416 : 17592186044416 35184372088832 : 35184372088832 70368744177664 : 70368744177664 140737488355328 : 140737488355328 281474976710656 : 281474976710656 562949953421312 : 562949953421312 1125899906842624 : 1125899906842624 2251799813685248 : 2251799813685248 4503599627370496 : 4503599627370496 9007199254740992 : 9007199254740992 18014398509481984 : 18014398509481984 36028797018963968 : 36028797018963968 72057594037927936 : 72057594037927936 144115188075855872 : 144115188075855872 288230376151711744 : 288230376151711744 576460752303423488 : 576460752303423488 1152921504606846976 : 1152921504606846976 2305843009213693952 : 2305843009213693952 4611686018427387904 : 4611686018427387904 9223372036854775808 : 9223372036854775808

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked

      Works perfect. I only changed the testing-lines for better visability:

      for (map 1 << $_, 0 .. 63) { print sprintf("%b", $_- 1 )," : ", sprintf("%b",nextPowerOfTwo( $_- +1 )); } $ perl leftmost_bit.pl 0 : 0 1 : 1 11 : 100 111 : 1000 1111 : 10000 ...

      Maybe it's fast enough versus log(),division,log() if inlined. But I am only prototyping the pure Perl version for a C implementation. But it would be nice to have pure Perl as a fallback.

        I am only prototyping the pure Perl version for a C implementation.

        If your doing it in C, it is very efficient compared to other methods; and works well as an Inline::C routine.

        It originates from Bit Twiddling Hacks.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
        In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
Re: reliable get position of leftmost bit in large integers
by hdb (Monsignor) on Mar 12, 2015 at 10:42 UTC

    Should your code not be:

    perl -e 'for my $i (1..63) {if(int(log(2**($i-1))/log(2)) != $i-1) {pr +int "$i\n"}}'

    which gives no output for me.

    UPDATE: Apologies, I think I get it now, the leading one of 2^i - 1 should be in the same place as the leading one for 2^(i-1) which is what you are testing.

    My next thought was to use length(sprintf("%b",$x)) but that does not work either:

    for my $i (1..63) { if(length(sprintf("%b",2**$i-1)) != length(sprintf("%b",2**($i-1)) +)) { print"$i\n"; printf("%b\n",2**($i-1)); printf("%b\n",(2**$i)-1); } }

      No, because the problem triggers on vectors filled with on-bits. But the exponentiation ** also seems to have a problem (thanks your remark):

      $ perl -e 'print sprintf("%b",2**53-1),"\n"' 11111111111111111111111111111111111111111111111111111 $ perl -e 'print sprintf("%b",2**54-1),"\n"' 1000000000000000000000000000000000000000000000000000000 $ perl -e 'print sprintf("%b",2**(53-1)),"\n"' 10000000000000000000000000000000000000000000000000000

        The problem is that ** returns a double (floating point), which explains the lack of precision. You should use 1<<54 like BrowserUk did in his answer.

Re: reliable get position of leftmost bit in large integers
by thargas (Deacon) on Mar 12, 2015 at 13:02 UTC
    You're using numeric operations, but you're asking questions about bit vectors. Wouldn't you be better off storing your bit vectors as bits with vec? Then you wouldn't have rounding errors caused by the numeric operations. Of course, if you're using them elsewhere as numbers, the conversion effort may not be worth it.
Re: reliable get position of leftmost bit in large integers
by AppleFritter (Vicar) on Mar 12, 2015 at 11:18 UTC
    There's Math::Int64 on CPAN -- I've never used it, but perhaps it'll suit your needs.
Re: reliable get position of leftmost bit in large integers
by syphilis (Archbishop) on Mar 12, 2015 at 12:34 UTC
    If $v1 and $v2 were expressed as 64-bit unsigned integers, I'd be tempted to investigate (untested):
    sub is_same_leading_bit { my ($v1, $v2) = (shift, shift); my $and = 1 << 63; for my $shift(0 .. 63) { my $c1 = ($v1 << $shift) & $and; my $c2 = ($v2 << $shift) & $and; return 1 if $c1 & $c2; return 0 if $c1 | $c2; } return 0; # both args are 0. }
    But I'd be writing the sub in C and accessing it via Inline::C or XS.

    Cheers,
    Rob

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1119766]
Approved by marto
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (2)
As of 2024-04-26 04:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found