Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

How can I set a bit to 0 ?

by bartender1382 (Beadle)
on May 26, 2022 at 16:09 UTC ( #11144200=perlquestion: print w/replies, xml ) Need Help??

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

I feel silly asking this, but...

I know how to set bits, and check for them. What I don't know how to do, nor find on Google,is turn off a specific bit whether it is set or not.

my $stats = 0; $stats = upload | getTicket | downLoading; printCLine($stats);

Yes, I can reset every bit minus the one I want, but that feels kludgy

Replies are listed 'Best First'.
Re: How can I set a bit to 0 ?
by ikegami (Patriarch) on May 26, 2022 at 16:37 UTC

    Set:

    $flags |= 1 << $bit_num;

    Clear:

    $flags &= ~( 1 << $bit_num );

    Toggle:

    $flags ^= 1 << $bit_num;

    Read (returns a true value or a false value):

    $flags & ( 1 << $bit_num )

    It looks like you have constants that are already of the form 1 << $bit_num, so

    $stats &= ~downLoading; # No longer downloading.
Re: How can I set a bit to 0 ?
by stevieb (Canon) on May 26, 2022 at 20:56 UTC

    I prototype a lot of my C microcontroller code in Perl, and for that purpose so I, and others might benefit, I wrote Bit::Manip. That contains code that has to be compiled, so for a Pure Perl version, there's Bit::Manip::PP.

    It does all the things that we who have to do bitwise operations from time-to-time but not as a profession (or often enough to remember how) we need to do.

    Here's an example of turning 'off' a bit:

    Code:

    use strict; use warnings; use Bit::Manip qw(:all); my $num = 0b11100111; # or literal 231 my $bit_position = 6; my $flipped = bit_off($num, $bit_position); printf("orig: %b\n", $num); printf("flipped: %b\n", $flipped);

    Output:

    steve@maezi ~/scratch >perl bit.pl orig: 11100111 flipped: 10100111
Re: How can I set a bit to 0 ?
by Marshall (Canon) on May 27, 2022 at 00:19 UTC
    Your question is not at all silly. Good question.

    More often than as a bit number, these binary bits are defined in a "bit mask".
    It is important to understand the difference between just & (bit wise and) and && (high priority logical and).
    Also important is the difference between ~ (bit inverse) and ! (logical not).

    Complex binary manipulation can be difficult in the High Level Language (Perl or even C) because you don't know how many bits you are dealing with (16,32,64).
    However, these simple idioms suffice for most common cases.

    Some example code:

    use strict; use warnings; use constant { upload => 1, # same as 0b1 getTicket => 2, # same as 0b10 downLoading => 4, # same as 0b100 whatEver => 8, # same as 0b1000 moreEver => 16,# same as 0b10000 }; my $stats = upload | getTicket | downLoading | whatEver | moreEver; print "numeric representation in decimal of all bit set is: $stats\n\n +"; # check if getTicket is on? print "checking if getTicket is on?\n"; $stats & getTicket ? print "getTicket is ON\n\n" : print "getTicket is + OFF\n\n"; # turn getTicket off print "turning getTicket off...\n"; # note: we need bitwise "and, which is &" and ~ instead of logical ! $stats = $stats & ~getTicket; # ~ means bit inverse of bits print "numeric decimal value = $stats\n"; # check if getTicket is off? print "check if getTicket turned off?\n"; $stats & getTicket ? print "getTicket is ON\n\n" : print "getTicket is + OFF\n\n"; # flip status of just getTicket print "flipping state of getTicket...\n"; $stats = $stats ^ getTicket; print "numeric decimal value = $stats\n"; $stats & getTicket ? print "getTicket is ON\n\n" : print "getTicket is + OFF\n\n"; print "flipping state of getTicket...\n"; $stats = $stats ^ getTicket; print "numeric decimal value = $stats\n"; $stats & getTicket ? print "getTicket is ON\n\n" : print "getTicket is + OFF\n\n"; __END__ numeric representation in decimal of all bit set is: 31 checking if getTicket is on? getTicket is ON turning getTicket off... numeric decimal value = 29 check if getTicket turned off? getTicket is OFF flipping state of getTicket... numeric decimal value = 31 getTicket is ON flipping state of getTicket... numeric decimal value = 29 getTicket is OFF
    Update: I suppose that something like: use constant allBits => upload | getTicket | downLoading | whatEver | moreEver; is possible in favor of ~0 (which means turn all bits in the integer register ON). Often you don't want to cause any bits to be turned on which are not specified.

      Your question is not at all silly.

      Silly in the sense that have worked with bit masks in the past, and if I ever knew how to "clear one" I forgot it.

Re: How can I set a bit to 0 ? (Add Two Numbers Using Only Bitwise Operators)
by eyepopslikeamosquito (Bishop) on May 28, 2022 at 10:18 UTC

    Most languages are like stackoverflow: I have a question, I want the best answer. Perl is like PerlMonks: I have a doubt, I want to read an interesting discussion about it that is likely to go on a tangent. q-:

    -- tye in Re: What is PerlMonks? (why Perl)

    Poor old bartender1382 asking the simple and naive question "How can I set a bit to 0?" seems to have got more than he bargained for. Thanks Grumpy Gramps! :)

    For cheap thrills, let's try to steer this thread further off course with a couple of example programs to add numbers using only bitwise operators. Improvements, alternative implementations welcome. Note: this was just for fun, I've never had a genuine need to do this, interested to hear from folks who have.

    Perl Program to Add Two Numbers Using Only Bitwise Operators

    # Perl program to add two numbers using only bitwise operators # See https://stackoverflow.com/questions/4068033/add-two-integers-usi +ng-only-bitwise-operators use strict; use warnings; # See [id://11135535] (note that <C>use integer</C> subtly affects bit + operations in Perl) use integer; sub BitAdd1 { my ($aa, $bb) = @_; my $carry = $aa & $bb; my $result = $aa ^ $bb; while ($carry != 0) { my $s_carry = $carry << 1; $carry = $result & $s_carry; $result ^= $s_carry; } return $result; } sub BitAdd2 { my ($aa, $bb) = @_; $bb == 0 and return $aa; return BitAdd2($aa ^ $bb, ($aa & $bb) << 1); } for my $r ( [0, 1], [1, -1], [69, -42], [42, 69], [-42, 69], [-42, -69], [256, 512], [123456789, 1], [2147483647, 1], [-2147483648, 1] ) { my $sum0 = $r->[0] + $r->[1]; my $sum1 = BitAdd1($r->[0], $r->[1]); my $sum2 = BitAdd2($r->[0], $r->[1]); print "$r->[0] + $r->[1] = $sum0 ($sum1 $sum2)\n"; $sum0 == $sum1 or die "oops 1"; $sum0 == $sum2 or die "oops 2"; $sum1 == $sum2 or die "oops 3"; }

    Example run:

    0 + 1 = 1 (1 1) 1 + -1 = 0 (0 0) 69 + -42 = 27 (27 27) 42 + 69 = 111 (111 111) -42 + 69 = 27 (27 27) -42 + -69 = -111 (-111 -111) 256 + 512 = 768 (768 768) 123456789 + 1 = 123456790 (123456790 123456790) 2147483647 + 1 = 2147483648 (2147483648 2147483648) -2147483648 + 1 = -2147483647 (-2147483647 -2147483647)

    C++ Program to Add Two Numbers Using Only Bitwise Operators

    // C++ Program to add two numbers without using arithmetic operator. // bitadd.cpp // Built with : g++ -o bitadd -std=c++11 -Wall -O3 bitadd.cpp #include <cstddef> #include <cstdint> #include <vector> #include <utility> #include <iostream> #include <fstream> #include <sstream> // uncomment one of these // typedef int32_t my_int; typedef int64_t my_int; my_int Add(my_int x, my_int y) { // Iterate till there is no carry while (y != 0) { // carry now contains common set bits of x and y my_int carry = x & y; // Sum of bits of x and y where at least one of the bits is no +t set x = x ^ y; // Carry is shifted by one so that adding it to x gives requir +ed sum y = carry << 1; } return x; } using my_list_type = std::vector< std::pair<my_int, my_int> >; int main() { my_list_type ops = { { 0, 1 }, { 1, -1 }, { 69, -42 }, { 42, 69 }, { -42, 69 }, { -42, -69 }, { 256, 512 }, { 123456789, 1 }, { 2147483647, 1 }, { -2147483648, 1 } }; my_int sum0, sum1; std::cout << "sizeof my int = " << sizeof(my_int) << "\n"; for (const auto& c : ops) { sum0 = c.first + c.second; sum1 = Add(c.first, c.second); if (sum0 != sum1) { std::cout << "oops!\n"; } std::cout << c.first << " + " << c.second << " = " << sum0 << " +(" << sum1 << ")\n"; } return 0; }

    Example run:

    sizeof my int = 8 0 + 1 = 1 (1) 1 + -1 = 0 (0) 69 + -42 = 27 (27) 42 + 69 = 111 (111) -42 + 69 = 27 (27) -42 + -69 = -111 (-111) 256 + 512 = 768 (768) 123456789 + 1 = 123456790 (123456790) 2147483647 + 1 = 2147483648 (2147483648) -2147483648 + 1 = -2147483647 (-2147483647)

    See Also

      Note that integer has lexical scope and so could be used within the definition of a subroutine to limit the effects (and side effects) of the pragma to operations within the subroutine.


      Give a man a fish:  <%-{-{-{-<

Re: How can I set a bit to 0 ?
by kcott (Archbishop) on May 26, 2022 at 17:34 UTC

    G'day bartender1382,

    "What I don't know how to do ... turn off a specific bit whether it is set or not."

    Update: This wasn't a good solution (see reply below). The following would have been better:

    #!/usr/bin/env perl use strict; use warnings; my $var1 = 0b00010100; my $var2 = 0b00000110; my $fmt = "%08b\n"; print "Initial:\n"; printf $fmt, $var1; printf $fmt, $var2; for my $bit (0, 1, 4) { print "Bit $bit off:\n"; $var1 &= ~(1 << $bit); printf $fmt, $var1; $var2 &= ~(1 << $bit); printf $fmt, $var2; }

    I've stricken the original code; it's in the spoiler. The output is unchanged.

    Output:

    Initial: 00010100 00000110 Bit 0 off: 00010100 00000110 Bit 1 off: 00010100 00000100 Bit 4 off: 00000100 00000100

    — Ken

      Usually you are right on the money with excellent replies and advice. In this case you've missed by a country mile, especially in light of ikegami's excellent reply an hour earlier.

      Like stevieb, my experience writing embedded C++ firmware informs my familiarity with bit manipulation so I construct appropriate bit manipulation expressions almost without thought. The thought of using a sub to do what amounts to a handful of processor instructions even on RISC machines makes me shudder. I apologize for my reaction, but given the reply is from such an esteemed (at least by me) monk I felt the need to ensure the OP and other seekers of enlightenment shift their gaze to ikegami's reply.

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
        I apologize for my reaction

        Most certainly not needed here, especially with the complete diligence and respect shown throughout the entire post (and all of your posts).

        I, for one, am in complete agreement with what you've said. ikegami's posts here and wide regarding bitwise operations show pure expertise, and, in fact, the software I posted about below was partly based on his posts here, and outside of this site.

        G'day GrandFather,

        ++ Thanks for your thoughtful reply.

        I'll need to study this in more detail. I have an inkling of where I've cocked-up but I have to rush off to get my final Covid booster shot. I'll get back to this later today (side-effects permitting).

        — Ken

Re: How can I set a bit to 0 ?
by afoken (Canon) on May 26, 2022 at 23:28 UTC

    Using butterflies, of course!

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: How can I set a bit to 0 ?
by BillKSmith (Monsignor) on May 27, 2022 at 13:44 UTC
    The Perl function vec could eliminate the need for special masking and logic to access bits. Use pack/unpack if the traditional format is required for I/O or third party software. The learning curve is probably too steep for a small or one-time project, but it worth knowing that this tool is in our tool box. This type of code may be hard to write the (especially the first time we try), but should be easier to maintain because than traditional code because it more closely corresponds to our idea of what we want to do.
    Bill
      > vec could eliminate

      > Use pack/unpack

      The OP seems on a beginner level and you didn't demonstrate how this would be done.

      Even I can't remember ever using vec and pack/unpack force me to reread their perldocs again and again.

      So I'd appreciate if you could shown some example code solving the OP's question this way, please.

      I really don't know by myself and am always eager to learn.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        Lines 21 thru 23 of the following example demonstrate operations on individual bits, specified by their position, of a status word. The difficulty arises when we need to input or output this word. We need the bytes in the order that our hardware expects them ("endianness") and we want the position 0 to refer to the most significant bit.

        I created a disc file which contains nothing but a 16-bit status word and verified the exact contents of that file with the utility "xxd" (came packaged with vim). My example reads that status word, makes a few changes, and writes it back out to another file. Again I use xxd to verify that I made the changes that I intended.

        OK, I admit that lines 11 thru 19 and 25 thru 33 were developed empirically for this use of vec on my 64-bit intel machine. They are not portable to other environments but should work, without change, on any application on this or similar machine.

        use strict; use warnings; my $stats; open my $DEVICE_IN, "<", 'stat.dat' or die "Cannot open input device: $!"; $stats = <$DEVICE_IN>; close $DEVICE_IN; $stats = pack( 'b16', reverse( unpack( 'B16', $stats ) ) ); vec($stats, 2, 1) = 0; # Reset bit 2 vec($stats, 6, 1) = 1; # Set bit 6 vec($stats, 10, 1) ^= 1; # Toggle bit 10 $stats = pack( 'b16', reverse( unpack( 'B16', $stats ) ) ); open my $DEVICE_OUT, ">", 'newstat.dat' or die "Cannot open output device: $!"; print $DEVICE_OUT $stats; close $DEVICE_OUT;

        Here is the content of the two data files (displayed by xxd) after this example runs

        C:\Users\Bill\forums\monks>xxd stat.dat 00000000: f531 .1 C:\Users\Bill\forums\monks>xxd newstat.dat 00000000: d711 .. C:\Users\Bill\forums\monks>

        Note: There is no guarantee that traditional bit masking will eliminate this problem in I/O. It might, but testing is still required.

        Bill
Re: How can I set a bit to 0 ?
by LanX (Sage) on May 26, 2022 at 22:36 UTC
    > What I don't know how to do, nor find on Google,is turn off a specific bit whether it is set or not.

    you "mask" (using & the "bitwise-and") with a bitvector which is only 0 if and only if you want to turn off.

    DB<22> $in = 0b10101010 + + + DB<23> $mask = 0b11110000 # 1 keep 0 erase + + + DB<24> printf "%b", $in & $mask + 10100000
    for more see Mask (computing)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

Re: How can I set a bit to 0 ?
by harangzsolt33 (Friar) on May 28, 2022 at 02:19 UTC
    The XOR operator works just like the bitwise NOT operator (~) because it flips bits, but it takes two inputs. Input number 1 determines which bits will be negated in input number 2:

    For example, 0000 ^ 0000 will simply leave all the bits zero.
    1111 ^ 0000 will flip all the bits, so they become 1111.
    1000 ^ 0000 will flip only the first bit and leave the rest unchanged. Result: 1000
    1110 ^ 1101 will flip the first three bits and leave the last one unchanged. Result: 0011

    The AND operator turns specific bits OFF. It takes two input numbers. The bits in input number 1 determine which bits in input number will be turned off. The ones that are zero will be turned off. Here is an example:

    1001 ^ 0110 = 0000
    1100 ^ 0111 = 0100
    0000 ^ 1111 = 0000
    1000 ^ 1111 = 1000

    See, this is really simple. Here is a tiny example program:

    #!/usr/bin/perl -w use strict; use warnings; $a = 4; print "\n ORIGINAL = $a"; $a &= 0xFFFB; # 0xFFFB = 1111111111111011 binary print "\nCLEAR BIT = $a"; $a |= 4; print "\n SET BIT = $a"; print("\n XOR BIT = ", ($a ^ 0)); print("\n XOR BIT = ", ($a ^ 0xF)); exit;
      Input number 1 determines which bits will be negated in input number 2 ...

      Just a note of caution: This phrasing suggests that the order of the operands affects the result of the ^ (bitwise-xor) operator, but it does not. The order is irrelevant:

      Win8 Strawberry 5.8.9.5 (32) Sat 05/28/2022 0:35:22 C:\@Work\Perl\monks >perl use strict; use warnings; printf "%04b \n", 0b1100 ^ 0b0111; printf "%04b \n", 0b0111 ^ 0b1100; ^Z 1011 1011
      And likewise for the | (bitwise-or) and & (bitwise-and) operators.

      Update: Slight change to phrasing of first sentence for clarity.


      Give a man a fish:  <%-{-{-{-<

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2022-09-26 02:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I prefer my indexes to start at:




    Results (116 votes). Check out past polls.

    Notices?