0

I am trying to take a given unsigned char and store the 8 bit value in an unsigned char array of size 8 (1 bit per array index).

So given the unsigned char A

Id like to create an unsigned char array containing 0 1 0 0 0 0 0 1 (one number per index)

What would be the most effective way to achieve this? Happy Thanksgiving btw!!

NetVipeC
  • 4,402
  • 1
  • 17
  • 19
David Baez
  • 1,208
  • 1
  • 14
  • 26
  • 2
    use `ch & (1 << n)` to select a bit, and then set values in your array accordingly – M.M Nov 27 '14 at 20:28
  • binary and (&) and bit shifting for the win. Should post that as an answer instead of a comment Matt. – James H Nov 27 '14 at 20:30

3 Answers3

1

You could use bit operators as recommended.

#include <stdio.h>

main() {
    unsigned char input_data = 8;
    unsigned char array[8] = {0};
    int idx = sizeof(array) - 1;
    while (input_data > 0) {
        array[idx--] = input_data & 1;
        input_data /= 2; // or input_data >>= 1;
    }

    for (unsigned long i = 0; i < sizeof(array); i++) {
        printf("%d, ", array[i]);
    }
}
NetVipeC
  • 4,402
  • 1
  • 17
  • 19
  • Why not a simple `input_data >>= 1`? – amaurea Nov 27 '14 at 20:47
  • Because long time ago, every compiler will optimize `input_data /= 2;` as you were written `input_data >>= 1;`, normally is most clear `/2` that shift operation, people understand better division that shift, in this case working with bits it's probably not much problem. – NetVipeC Nov 27 '14 at 20:51
  • 1
    I disagree. What you are logically doing is shifting the bit into the next position so that it can be read out using &1. Division by two is just an obfuscated way of writing that. – amaurea Nov 27 '14 at 20:52
  • It's the same, in one case you are shifting a bit (sometimes between new C++ programmer the question of what happen with the bit shifted remain) and in the other dividing by 2 (the base of binary numbers, you are taking out the last digit in the binary representation). There's no problem, write as you like. Andrei Alexandrescu give the recommendation of using `/2` instead of shifting in a C++ conference in Facebook a year ago I think. The historical use of `>>= 1` is that in assembly there is an specific instruction for `shifting by 1` and old, old compilers don't used when you write `/2` – NetVipeC Nov 27 '14 at 20:57
  • 1
    I understand what it's doing. I am disagreeing with you about what is clearest. When what you are logically doing is a division, then use division. When what you are logically doing is shifting bits, then use a bitshift operation. By writing this as a division you are hiding the intent of the operation. Just because you've read advice about not abusing bitshifts to do divisions doesn't mean that one should never use bitshifts! – amaurea Nov 27 '14 at 21:03
  • 1
    _Store as int_? What do you mean? – NetVipeC Nov 27 '14 at 21:23
1

Take the value, right shift it and mask it to keep only the lower bit. Add the value of the lower bit to the character '0' so that you get either '0' or '1' and write it into the array:

unsigned char val = 65;
unsigned char valArr[8+1] = {};

for (int loop=0; loop<8; loop++)
    valArr[7-loop] = '0' + ((val>>loop)&1);

printf ("val = %s", valArr);
Chris
  • 2,655
  • 2
  • 18
  • 22
1

The fastest (not sure if that's what you menat by "effective") way of doing this is probably something like

void char2bits1(unsigned char c, unsigned char * bits) {
    int i;
    for(i=sizeof(unsigned char)*8; i; c>>=1) bits[--i] = c&1;
}

The function takes the char to convert as the first argument and fills the array bits with the corresponding bit pattern. It runs in 2.6 ns on my laptop. It assumes 8-bit bytes, but not how many bytes long a char is, and does not require the input array to be zero-initialized beforehand.

I didn't expect this to be the fastest approach. My first attempt looked like this:

void char2bits2(unsigned char c, unsigned char * bits) {
    for(;c;++bits,c>>=1) *bits = c&1;
}

I thought this would be faster by avoiding array lookups, by looping in the natural order (at the cost of producing the bits in the opposite order of what was requested), and by stopping as soon as c is zero (so the bits array would need to be zero-initialized before calling the function). But to my surprise, this version had a running time of 5.2 ns, double that of the version above.

Investigating the corresponding assembly revealed that the difference was loop unrolling, which was being performed in the former case but not the latter. So this is an illustration of how modern compilers and modern CPUs often have surprising performance characteristics.

Edit: If you actually want the unsigned chars in the result to be the chars '0' and '1', use this modified version:

void char2bits3(unsigned char c, unsigned char * bits) {
    int i;
    for(i=sizeof(unsigned char)*8; i; c>>=1) bits[--i] = '0'+(c&1);
}
amaurea
  • 4,950
  • 26
  • 35