简体   繁体   中英

How to use signed or unsigned integer for bit mapping in C?

I want to reset the 31st bit (last bit, 0 to 31 range) of int32_t, only this case seems to fail. ie, Output failed for the case when 'i' is 31, it's returning -1. What is the error and how do I resolve this?

#include <stdio.h>
#include <stdlib.h>

void Release(int ResetBit, int32_t *Val)
{
    int32_t testBit = 1; /* XoR Bit */

    if (ResetBit >= 0 && ResetBit < 32)
    {
        testBit = (testBit << ResetBit);
        *Val ^= testBit;
    }
    else
    {
        perror("Max range is 0-31 only, failed! ");
        //exit(1);
    }  
}

int main(int argc, char const *argv[])
{
    int count = 0;
    for (int i = 0; i < 32; i++)
    {
        int32_t MaxValue = 2147483647;
        Release(i, &MaxValue);
        printf("MaxValue = %d NodeID = % d\n", MaxValue, i);
        count++;
   }
    printf("%d", count);
    return 0;
}

Output for the case i = 31 is:

MaxValue = -1 NodeID = 31

First of all: Don't use signed integers for bitmaps. Always use unsigned . The reason for that is that bit shifting on signed integers may result in undefined behavior while shifting unsigned integers are always safe.

Secondly: You are using XOR in the Release function. XOR with testBit will not clear a bit. XOR will toggle the bit value, ie 1 becomes 0 and 0 becomes 1. Instead you want: *Val &= ~testBit; It works like:

If testBit is    0000.0000.0000.0000.0000.0000.0000.1000
then ~testbit is 1111.1111.1111.1111.1111.1111.1111.0111
then *Val &= ... will clear bit number 3 and keep all other unchanged 
as `&` is a bitwise AND operation.

When using unsigned remember to change the printf to print an unsigned instead of using %d , ie like printf("%" PRIu32 "\\n", uint32t_variable); .

EDIT

What went wrong with the XOR?

Let's assume that you are using uint32_t and XOR, then this will happen:

Your input is

0111.1111.1111.1111.1111.1111.1111.1111

and you XOR with

1000.0000.0000.0000.0000.0000.0000.0000

which toggles bit 31 resulting in

1111.1111.1111.1111.1111.1111.1111.1111

The function was supposed to clear bit 31 but it didn't. XOR is just not the correct operator for that.

If you don't need an actual signed type, use uint32_t and all problems will go away. The problem with using bitwise operators on signed types is various forms of poorly-defined behavior.

For example, left-shifting something into the sign bit of a int32_t leads to undefined behavior, meaning a potential bug in case your compiler doesn't cover that case with a non-standard extension. Similarly, right-shifting a negative number can either lead to arithmetic or logic shift, the C standard doesn't specify which one, but allows both forms.

That being said, if you simply wish to set/clear bit 31 of an int32_t , it's well-defined to do so like this:

int32_t i32 = ...;
i32 |= 1u << 31;    // set MSB
i32 &= ~(1u << 31); // clear MSB
i32 ^= 1u << 31;    // toggle MSB

Where the u is ensuring unsigned arithmetic.

Use the correct bitwise operation. to reset bit use &

int32_t ResetBit(int bit, int32_t *val)
{
    uint32_t mask = ~(1UL << bit);

    *val &= mask;
    return *val;
}

and usage:

void printnitd(int32_t val)
{
    for(uint32_t mask = 1UL << 31; mask; mask >>= 1)
    {
        printf("%c",  (val & mask) ? '1' : '0');
    }
}

int main(void)
{
    for(int bit = 0; bit < 32; bit++)
    {
        int32_t a = -1;
        printf("bit %u = ", a);
        printnitd(ResetBit(bit, &a));
        printf("\n");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM