1

My goal with this program is that I want to be able to set 1000 bits to 0 or 1 separately, for this I'm using an array of 128 bit integers.

Now the issue is when I simply clear_bit(3), bit 35 is also being cleared (and vice versa). 3 & 35 are always cleared together, same for 4 & 36, 5 & 37 etc. So there's clearly a pattern. I simply want 3 to be cleared while the rest stays 1.

Any ideas why this is happening? Thanks!

#include <stdio.h>

__uint128_t array [(1000/128) + 1];

// Set bit to 0.
void clear_bit(int k)                
{
    array[k/128] &= ~(1 << (k%128));
}

// Find the value of bit.
int test_bit(int k)
{
    return((array[k/128] & (1 << (k%128) )) != 0);     
}

// Set bit to 1.
void set_bit(int k)
{
    array[k/128] |= 1 << (k%128);  // Set the bit at the k-th position in A[i]
}

int main (void)
{
    // Set all bits to 1.
    for(int i = 0; i < 40; i++) {
        set_bit(i);
    }

    // I want to clear bit 3, but it also clears 35 for an unknown reason.
    clear_bit(3);

    for(int i = 0; i < 40; i++) {
        printf("%d is now:%d\n", i, test_bit(i));
    }

    return (0);
}

result

SJ19
  • 1,933
  • 6
  • 35
  • 68
  • 1
    No part of `~(1 << (k%128))` is __uint128_t math. If you want to use __uint128_t's, you need to be careful about how you mix ints in with those. – user2357112 Sep 30 '17 at 17:06
  • `1 << (k%128)` --> `((__uint128_t)1) << (k%128)`. IMO, use `unsigned` – chux - Reinstate Monica Sep 30 '17 at 17:13
  • @chux Wow thanks dude, you solved my whole problem! Put it in an answer and I'll accept it. ;) Also, what do you mean "use unsigned", we're already using unsigned int128 aren't we? – SJ19 Sep 30 '17 at 17:18
  • 1) Consider `#define UNS_WIDTH (CHAR_BIT*sizeof(unsigned)) unsigned array [(1000 + UNS_WIDTH - 1)/UNS_WIDTH]; ... array[k/UNS_WIDTH] &= 1u << (k%UNS_WIDTH);` – chux - Reinstate Monica Sep 30 '17 at 17:29
  • I would use myself the native size of the integer for your platform (32 or 64) instead. . – 0___________ Sep 30 '17 at 17:32

2 Answers2

1

OP's code is not using 128-bit math with 1 << (k%128). @user2357112

Insure the integer math is done with at least 128-bit math.

array[k/128] &= ~(1 << (k%128));
array[k/128] &= ~((__uint128_t)1 << (k%128));

Alternative, use unsigned in a portable fashion should unsigned have a bit width of be 16,32 64, 36, etc. No need to rely on __uint128_t.

#include <limits.h>
#define UNS_WIDTH (sizeof(unsigned)*CHAR_BIT)

unsigned array[(1000 + UNS_WIDTH - 1)/UNS_WIDTH];

void clear_bit(int k) {
  array[k/UNS_WIDTH] &= ~(1u << (k%UNS_WIDTH));
}

Best to insure the 1 is unsigned with 1u.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

I would suggest using the native size of platform registers. just compare the generated code (in this example 64 bit platform) - https://godbolt.org/g/Y316vU

__uint128_t array [(1000/128) + 1];
uint64_t array1[(1000/64) + 1];

void assignBit64(unsigned bit, unsigned value)
{
    array1[bit >> 6] &= ~((uint64_t)1 << (bit & 63));
    array1[bit >> 6] |= ((uint64_t)!!value) << (bit & 63);
}

void assignBit128(unsigned bit, unsigned value)
{
    array[bit >> 7] &= ~((__uint128_t)1 << (bit & 127));
    array[bit >> 7] |= ((__uint128_t)(!!value)) << (bit & 127);
}


void resetBit64(unsigned bit)
{
    array1[bit >> 6] &= ~((uint64_t)1 << (bit & 63));    
}

void resetBit128(unsigned bit)
{
    array[bit >> 7] &= ~((__uint128_t)1 << (bit & 127));
}

void setBit64(unsigned bit)
{
    array1[bit >> 6] |= ((uint64_t)1 << (bit & 63));    
}

void setBit128(unsigned bit)
{
    array[bit >> 7] |= ((__uint128_t)1 << (bit & 127));
}
0___________
  • 60,014
  • 4
  • 34
  • 74