Bitwise help needed

  • I'm looking for the single line statement that will explicitly set a specific bit position in an integer field to the bit value (i.e., 0 or 1) of a bit parameter and leave all other bits unchanged.

    I know that that I can test the value of the bit parameter and then use an IF..THEN..ELSE with a bitwise OR (|) to explicitly turn it on and a bitwise AND NOT (& ~) to explicitly turn it off. But I'm looking for something that will not require an IF statement.

    The bit to be set is known so providing a constant as part of the equation is not a problem.

    Example values modifying the fourth bit:

    DECLARE @i int;

    DECLARE @b-2 bit;

    SET @i = {logic here} equivalent to: SET @i = @i + @b-2

    @i @b-2 Desired result

    0 0 0

    0 1 8

    23 0 23

    23 1 31

    31 0 23

    31 1 31


    [font="Arial Narrow"](PHB) I think we should build an SQL database. (Dilbert) What color do you want that database? (PHB) I think mauve has the most RAM.[/font]

  • I have to ask, is there any reason to not simply add 8 * @b-2 to @i?

    declare @i int, @b-2 bit;

    select @i = 0, @b-2 = 1;

    select @i + (@b * 8);

    Seems to do what you're looking for.

    - Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
    Property of The Thread

    "Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon

  • GSquared (8/19/2009)


    I have to ask, is there any reason to not simply add 8 * @b-2 to @i?

    declare @i int, @b-2 bit;

    select @i = 0, @b-2 = 1;

    select @i + (@b * 8);

    Seems to do what you're looking for.

    It doesn't match my desired results. I.e., it doesn't work all of the time. E.g., if the current value is 31 (bit 4 is already on) and a value of 1 (true) is passed in, the result will be 39 and not 31. In this case, the bit setting should essentially be a NO-OP.

    Conversely, if the current value is 31 (bit 4 is already on) and a value of 0 (false) is passed in, the result will be 31 and not 23.


    [font="Arial Narrow"](PHB) I think we should build an SQL database. (Dilbert) What color do you want that database? (PHB) I think mauve has the most RAM.[/font]

  • How about the following formula?

    DECLARE @bitPos TINYINT

    SET @bitPos = 4 -- EDIT: was 3

    SELECT @i - ( ( FLOOR(@i/(POWER(2,(@bitPos-1)))) % 2 - @b-2) * (POWER(2,(@bitPos-1))))

    Some explanation:

    Step 1:

    @bitPos is used to be able to modify any requested bit position (please note that there is no test for valid data, e.g. negative values...)

    Step 2:

    POWER(2,(@bitPos-1)) will return the integer value of the @bitPos variable (8 in the given example for bit position 4) [EDIT]: see note

    Step 3:

    FLOOR(@i/POWER(2,(@bitPos-1))) will give you the BIT pattern starting with the bit matching @bitPos of the binary structure (4 in this case). It actually eliminates bit 1 to 3 of @i.

    Example:

    FLOOR (24/8) will return 3, which is 0011 in binary format.

    FLOOR (17/8) will return 2, which is 0010 in binary format.

    Step 4:

    %2 will eliminate every bit except the first one, leaving 1 for @i = 24 and 0 for @i = 17.

    Step 5:

    subtracting @b-2 will result in 0, if the bit in question is already at the requested state, -1 if it needs to be turned from 0 to 1 and 1 if it needs to be turned from 1 to 0.

    Step 6:

    Multiply the result by the integer value represented by the bit position @bitPos. This is to respect that we're working on a specific bit (bit 4 in this case).

    Step 7:

    Modify @i:

    Turning the bit from 0 to 1 means add 8, turn it from 1 to 0 means subtract 8.

    Edit: bad mistake: bit position is NOT equal to the power value (explanation didn't match code provided). Function and description modified... :blush:



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

  • noone has mentioned the TSQL bitwise operators yet. pipe (|)and carat(^) for flipping bits, and ampersand for comparisons(&)

    here's an example for you all:

    declare @myBitField int

    SET @myBitField = 42 --meaning of life

    --bit value 3 = True,

    --bit value 5 = True, all others are false

    --turn on bit 12

    --first line to uncomment to test

    --SET @myBitField = @myBitField | 4096 --pipe character is bitwise OR

    --turn Off bit 3

    --second line to uncomment to test

    --SET @myBitField = @myBitField ^ 8

    --SELECT '@myBitField: ' + CHAR(13) +

    'bit value 1 '+ MIN(CASE @myBitField & 1 WHEN 1 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 2 '+ MIN(CASE @myBitField & 4 WHEN 4 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 3 '+ MIN(CASE @myBitField & 8 WHEN 8 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 4 '+ MIN(CASE @myBitField & 16 WHEN 16 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 5 '+ MIN(CASE @myBitField & 32 WHEN 32 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 6 '+ MIN(CASE @myBitField & 64 WHEN 64 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 7 '+ MIN(CASE @myBitField & 128 WHEN 128 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 8 '+ MIN(CASE @myBitField & 256 WHEN 256 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 9 '+ MIN(CASE @myBitField & 512 WHEN 512 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 10 '+ MIN(CASE @myBitField & 1024 WHEN 1024 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 11 '+ MIN(CASE @myBitField & 2048 WHEN 2048 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 12 '+ MIN(CASE @myBitField & 4096 WHEN 4096 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 13 '+ MIN(CASE @myBitField & 32768 WHEN 32768 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 14 '+ MIN(CASE @myBitField & 4194304 WHEN 4194304 THEN 'True' ELSE 'False' END) + CHAR(13) +

    'bit value 15 '+ MIN(CASE @myBitField & 1073741824 WHEN 1073741824 THEN 'True' ELSE 'False' END) + CHAR(13) + CHAR(13)

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • Lowell (8/19/2009)


    noone has mentioned the TSQL bitwise operators yet. pipe (|)and carat(^) for flipping bits, and ampersand for comparisons(&)

    The OP mentioned it in the first post:

    JohnG (8/19/2009)


    I know that that I can test the value of the bit parameter and then use an IF..THEN..ELSE with a bitwise OR (|) to explicitly turn it on and a bitwise AND NOT (& ~) to explicitly turn it off. But I'm looking for something that will not require an IF statement.

    The sample you provided still requires a test whether the bit needs to be turned on or off, leading to two different statements (one with pipe and the other one with carat). Those two statements can be wrapped in a CASE condition, meeting the OP's requirement (no IF condition). 😉

    What I was trying to do is to come up with a single statement that doesn't need to test for the bit value.



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

  • How about this

    (@i & 0xFFFFFFF7) ^ (@b * 8)

    Far away is close at hand in the images of elsewhere.
    Anon.

  • Thanks Lutz;

    I was under the impression he didn't have a handle on the bit operator yet.

    your example toggles the bit, right? mine was lamer and tries to set the bit, and it may already be set, so would have no effect.

    I thought if he knows which specific bit he wants to turn on or off, he can use my examples like this:

    --turn on bit 12

    SET @myBitField = @myBitField | 4096 --pipe character is bitwise OR

    --turn Off bit 3

    --SET @myBitField = @myBitField ^ 8

    if he was using a function or something, to pass which bit, ie 1 thru 15, i was thinking maybe something like this:

    declare @WhichBitToFlip int,@OnOrOff int

    SET @WhichBitToFlip =12

    SET @OnOrOff = 1--True/On

    --turn on bit 12

    If @OnOrOff != 0

    SET @myBitField = @myBitField | POWER(2,@WhichBitToFlip ) --4086

    Else

    SET @myBitField = @myBitField ^ POWER(2,@WhichBitToFlip ) --4086

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • VERY COOL David!

  • Thanks to all of the contributors to my post.

    Lowell (8/19/2009)


    noone has mentioned the TSQL bitwise operators yet. pipe (|)and carat(^) for flipping bits, and ampersand for comparisons(&)

    Yes, I DO have a handle on all of the bitwise operators. I was just looking for an elegant, single statement, method to replace the following:

    [font="Tahoma"]UPDATE MyTable SET

    PropertyMask =

    CASE @BitValue

    WHEN 0 THEN PropertyMask & ~ 8

    WHEN 1 THEN PropertyMask | 8

    END

    WHERE ...[/font]

    Although the above code works fine, I figured that there was some algorithm just beyond my mental grasp that would do it with a single statement. It was more of a "technical challenge" than a blocking problem.

    lmu92 (8/19/2009)


    How about the following formula?

    DECLARE @bitPos TINYINT

    SET @bitPos = 4 -- EDIT: was 3

    SELECT @i - ( ( FLOOR(@i/(POWER(2,(@bitPos-1)))) % 2 - @b-2) * (POWER(2,(@bitPos-1))))

    Lutz,

    Although it solves the problem, having to provide a page of code comments (i.e., documentation) in the stored procedure so that the next developer can understand what is happening is a deal-breaker.

    David Burrows (8/20/2009)


    How about this

    (@i & 0xFFFFFFF7) ^ (@b * 8)

    David,

    That is exactly what I was looking for. I.e., some combination of bitwise operands. However, instead of providing 0xFFFFFFF7 as a constant for the 4th bit, one can use ~ 8. So the formula becomes:

    [font="Arial Black"](@i & ~ 8) ^ (@b * 8)[/font]

    Note: There is a bug in the SQLServerCentral code formatter. The bitwise NOT operator ~ (tilde) in blocks appears as ' (single quote). Hence the nonuse of blocks in my examples.


    [font="Arial Narrow"](PHB) I think we should build an SQL database. (Dilbert) What color do you want that database? (PHB) I think mauve has the most RAM.[/font]

  • John,

    you could leave out the comment for the next developer. It's pretty much self explaining, isn't it? 😉

    I, personally, would have added much less explanation in a procedure or documentation since I'd expect that somebody who needs to be familiar with the complete business case should also be familiar with the methods I used...

    However, I have to admit: Davids solution is much more cleaner. But I wouldn't leave in a code without any explanation either. How long that documentation is, is up to you...

    Again: congrats to David. Good job!



    Lutz
    A pessimist is an optimist with experience.

    How to get fast answers to your question[/url]
    How to post performance related questions[/url]
    Links for Tally Table [/url] , Cross Tabs [/url] and Dynamic Cross Tabs [/url], Delimited Split Function[/url]

Viewing 11 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic. Login to reply