user2864293 user2864293 - 1 month ago 22
C++ Question

Implicit conversion or cast?

I have a function that interleaves the bits of 32 bit words and returns a 64 bit result. For this simple test case, the bottom 3 bytes are correct, and the contents of the top 5 bytes are incorrect. intToBin_32 and intToBin_64 are convenience functions to see the binary representation of the arguments and return val. I've placed casts from the 32 bit type to the 64 bit type everywhere I think they are needed, but I'm still seeing this unexpected (to me, at least) behavior. Is there an implicit conversion going on here, or is there some other reason this doesn't work correctly?

#include <stdint.h>
#include <stdio.h>

struct intString_32 {char bstr [32 + 1 + 8];};
struct intString_64 { char bstr [64 + 1 + 8];};

intString_32 intToBin_32(int a)
{
intString_32 b;

for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 5; j++)
{
if (j != 4)
{
b.bstr[5*i + j] = * ((a & (1 << (31 - (4*i + j)))) ? "1" : "0");
}

else
{
b.bstr[5*i + j] = 0x20;
}
}
}

b.bstr[40] = * ( "\0" );

return b;
}

intString_64 intToBin_64(long long a)
{
intString_64 b;

for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 9; j++)
{
if (j != 8)
{
b.bstr[9*i + j] = * ((a & (1 << (63 - (8*i + j)))) ? "1" : "0");
}

else
{
b.bstr[9*i + j] = 0x20;
}
}
}

b.bstr[72] = * ( "\0" );
return b;
}

uint64_t interleaveBits(unsigned int a, unsigned int b)
{
uint64_t retVal = 0;

for (unsigned int i = 0; i < 32; i++)
{
retVal |= (uint64_t)((uint64_t)((a >> i) & 0x1)) << (2*i);
retVal |= (uint64_t)((uint64_t)((b >> i) & 0x1)) << (2*i + 1);
}

return retVal;
}

int main(int arc, char* argv)
{
unsigned int foo = 0x0004EDC7;
unsigned int bar = 0x5A5A00FF;
uint64_t bat = interleaveBits(foo, bar);

printf("foo: %s \n", intToBin_32(foo).bstr);
printf("bar: %s \n", intToBin_32(bar).bstr);
printf("bat: %s \n\n", intToBin_64(bat).bstr);
}

Answer

Through debugging I noticed it's your intToBin_64 which is wrong, to be specific, in this line:

b.bstr[9*i + j] = * ((a & (1 << (63 - (8*i + j)))) ? "1" : "0");

take a closer look on the shift:

(1 << (63 - (8*i + j)))

The literal 1 is a integer, however, shifting a integer by more than 31 bits is undefined behavior. Shift a longlong instead:

b.bstr[9*i + j] = * ((a & (1ll << (63 - (8*i + j)))) ? "1" : "0");
Comments