1

I want to reset the 31st bit (last bit, 0 to 31 range) of int32_t, only this case seems to fail. i.e., 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

SatKetchum
  • 71
  • 1
  • 9
  • 2
    Well, don't use signed integers for this (shifting signed integers may resolve in undefined behavior). Use unsigned, i.e. `uint32_t`. Besides that it's not fully clear what you mean by `Release` - is it "set to zero" or is it "toggle the bit"? – Support Ukraine Oct 12 '20 at 07:55
  • I want "set to zero" a particular bit (in the range 0-31). To set a particular bit to zero, I am passing 'i' as a bit position. Eg: to set 5th bit to zero, i = 4. – SatKetchum Oct 12 '20 at 07:58
  • @4386427 I tried with u_int32_t, it gives the same error – SatKetchum Oct 12 '20 at 08:01
  • 1
    https://stackoverflow.com/questions/4009885/arithmetic-bit-shift-on-a-signed-integer#:~:text=As%20of%20c%2B%2B20,b%20%2C%20rounded%20down%20(ie. – Mat Oct 12 '20 at 08:01
  • 1
    There is nothing wrong with the **result**, it is your **signed value with all bits set**. If you want to see unsigned use `unsigned` **and** print with `%u`. – Antti Haapala -- Слава Україні Oct 12 '20 at 08:10
  • @AnttiHaapala Except `testBit = (testBit << 31);` relies on UB to get that result. So in theory it could print anything. In practice there's likely some non-standard extension guaranteeing deterministic behavior for this case. – Lundin Oct 12 '20 at 08:25
  • @Lundin in theory yes, but in practice, the compilers OP is likely to use this would actually have it defined (GCC, Clang, MSVC), and the problem is that OP expected to see a instead of -1. – Antti Haapala -- Слава Україні Oct 12 '20 at 08:26

3 Answers3

2

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, i.e. 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, i.e. 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.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
1

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Isn't this conflicting with https://stackoverflow.com/a/9498852/4386427 – Support Ukraine Oct 12 '20 at 09:23
  • @4386427 Why would it? That post is about a different topic of implicit promotion/conversion. – Lundin Oct 12 '20 at 09:57
  • Isn't there an unsigned to signed conversion here? And wouldn't that fall into #3 of the last part of the linked answer? – Support Ukraine Oct 12 '20 at 10:00
  • @4386427 Yes there is a lvalue conversion from signed to unsigned and it's implementation-defined - mostly because C allows exotic signedness formats. In practice, the impl.-defined part is "this computer uses 2's complement", but otherwise behaves deterministically on every compiler I've ever used. Meaning that the unsigned number gets converted to the raw binary equivalent in 2's complement. – Lundin Oct 12 '20 at 10:17
1

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");
    }
}
0___________
  • 60,014
  • 4
  • 34
  • 74