44

I have a BitArray with the length of 8, and I need a function to convert it to a byte. How to do it?

Specifically, I need a correct function of ConvertToByte:

BitArray bit = new BitArray(new bool[]
{
    false, false, false, false,
    false, false, false, true
});

//How to write ConvertToByte
byte myByte = ConvertToByte(bit);
var recoveredBit = new BitArray(new[] { myByte });
Assert.AreEqual(bit, recoveredBit);
Igor Kustov
  • 3,228
  • 2
  • 34
  • 31
Graviton
  • 81,782
  • 146
  • 424
  • 602

9 Answers9

67

This should work:

byte ConvertToByte(BitArray bits)
{
    if (bits.Count != 8)
    {
        throw new ArgumentException("bits");
    }
    byte[] bytes = new byte[1];
    bits.CopyTo(bytes, 0);
    return bytes[0];
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 17
    Mind: this computes the bits in reverse order, e.g. the BitArray from the example will convert into 128, not 1! – tehvan Feb 18 '09 at 07:41
  • Why does this happen in a reverse order? – Kornelije Petak Feb 04 '10 at 19:39
  • 1
    @kornelijepetak: That's just the way that BitArray works, in terms of the way it chooses to copy values. – Jon Skeet Feb 04 '10 at 19:43
  • 4
    @kornelijepetak: It is important that it copies in reverse order. If you use BitConverter on other types they are stored in little-endian format. –  Oct 04 '10 at 18:25
  • 3
    Its important to draw the distinction between byte endianness and bit endianness. Bit endianness tells you the ordering of the bits in each byte and whether the first bit is the most or least significant bit. Byte endianness tells you the expected order of the bytes in a word. Bit endianess is usually always described as "LSB first" or "MSB first" rather than little-endian or big-endian... – Tim Nov 11 '14 at 12:01
  • 2
    To reverse the order: `var reversed = new BitArray(bitArray.Cast().Reverse().ToArray());` – Maxence Sep 20 '15 at 08:20
39

A bit late post, but this works for me:

public static byte[] BitArrayToByteArray(BitArray bits)
{
    byte[] ret = new byte[(bits.Length - 1) / 8 + 1];
    bits.CopyTo(ret, 0);
    return ret;
}

Works with:

string text = "Test";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(text);
BitArray bits = new BitArray(bytes);
bytes[] bytesBack = BitArrayToByteArray(bits);
string textBack = System.Text.Encoding.ASCII.GetString(bytesBack);
// bytes == bytesBack
// text = textBack

.

Tedd Hansen
  • 12,074
  • 14
  • 61
  • 97
  • 15
    Instead of "bits.Length / 8", you should use "(bits.Length - 1) / 8 + 1", otherwise if the BitArray has a length of 7, your byte array will be empty. The "- 1" part makes sure a multiple of 8 will not return plus one. Thanks to http://stackoverflow.com/questions/17944/how-to-round-up-the-result-of-integer-division/503201#503201 – iano Nov 02 '11 at 22:03
  • 1
    Good point. A Math.Max(1, bits.Length / 8) will also work I guess (slightly more readable). I always operate on 8 bit bytes so I haven't considered the underflow condition. – Tedd Hansen Nov 15 '11 at 19:17
  • 2
    @TeddHansen What about 15? – Ark-kun Dec 01 '16 at 11:51
  • This doesn't handle the empty case, mind - might want to add a check for when `bits` is empty and return an empty `byte[]` array accordingly. – Extragorey Jul 05 '18 at 23:39
  • Should be "byte[(bits.Length - 1) / 8 - 1", otherwise adding unnecessary "0" byte end of byte array. – Güven Acar Sep 25 '18 at 10:43
  • The requirement here is that `bits.Length / 8` should round up, hence it should be `(int)Math.Ceiling[bits.Length / 8]`. We can simply this because we know that `bits.Length` is a positive integer: `(bits.Length + 8 - 1) / 8` == `(bits.Length + 7) / 8`. For `bits.Length == 0`, the formula conveniently returns `0`. – adyavanapalli Nov 27 '18 at 18:04
  • I use (bits.Length + 7) >> 3. This will always round up. – Ben Jaguar Marshall Jan 31 '19 at 01:38
9

A poor man's solution:

protected byte ConvertToByte(BitArray bits)
{
    if (bits.Count != 8)
    {
        throw new ArgumentException("illegal number of bits");
    }

    byte b = 0;
    if (bits.Get(7)) b++;
    if (bits.Get(6)) b += 2;
    if (bits.Get(5)) b += 4;
    if (bits.Get(4)) b += 8;
    if (bits.Get(3)) b += 16;
    if (bits.Get(2)) b += 32;
    if (bits.Get(1)) b += 64;
    if (bits.Get(0)) b += 128;
    return b;
}
tehvan
  • 10,189
  • 5
  • 27
  • 31
6

Unfortunately, the BitArray class is partially implemented in .Net Core class (UWP). For example BitArray class is unable to call the CopyTo() and Count() methods. I wrote this extension to fill the gap:

public static IEnumerable<byte> ToBytes(this BitArray bits, bool MSB = false)
{
    int bitCount = 7;
    int outByte = 0;

    foreach (bool bitValue in bits)
    {
        if (bitValue)
            outByte |= MSB ? 1 << bitCount : 1 << (7 - bitCount);
        if (bitCount == 0)
        {
            yield return (byte) outByte;
            bitCount = 8;
            outByte = 0;
        }
        bitCount--;
    }
    // Last partially decoded byte
    if (bitCount < 7)
        yield return (byte) outByte;
}

The method decodes the BitArray to a byte array using LSB (Less Significant Byte) logic. This is the same logic used by the BitArray class. Calling the method with the MSB parameter set on true will produce a MSB decoded byte sequence. In this case, remember that you maybe also need to reverse the final output byte collection.

testalino
  • 5,474
  • 6
  • 36
  • 48
LoxLox
  • 977
  • 9
  • 16
5

This should do the trick. However the previous answer is quite likely the better option.

    public byte ConvertToByte(BitArray bits)
    {
        if (bits.Count > 8)
            throw new ArgumentException("ConvertToByte can only work with a BitArray containing a maximum of 8 values");

        byte result = 0;

        for (byte i = 0; i < bits.Count; i++)
        {
            if (bits[i])
                result |= (byte)(1 << i);
        }

        return result;
    }

In the example you posted the resulting byte will be 0x80. In other words the first value in the BitArray coresponds to the first bit in the returned byte.

Caleb Vear
  • 2,637
  • 2
  • 21
  • 20
  • 1
    @Tvde1 `result` is never left shifted. Bits that are set are individually left shifted the correct amount `i` and then a bitwise or is done with result. A more verbose way of writing that line is: `result = result | ((byte)(1 << i))`. – Caleb Vear Mar 14 '22 at 13:14
  • Never mind my comment :) – Tvde1 Mar 15 '22 at 13:04
2

That's should be the ultimate one. Works with any length of array.

private List<byte> BoolList2ByteList(List<bool> values)
    {

        List<byte> ret = new List<byte>();
        int count = 0;
        byte currentByte = 0;

        foreach (bool b in values) 
        {

            if (b) currentByte |= (byte)(1 << count);
            count++;
            if (count == 7) { ret.Add(currentByte); currentByte = 0; count = 0; };              

        }

        if (count < 7) ret.Add(currentByte);

        return ret;

    }
1

In addition to @JonSkeet's answer you can use an Extension Method as below:

public static byte ToByte(this BitArray bits)
{
    if (bits.Count != 8)
    {
        throw new ArgumentException("bits");
    }
    byte[] bytes = new byte[1];
    bits.CopyTo(bytes, 0);
    return bytes[0];
}

And use like:

BitArray foo = new BitArray(new bool[]
{
    false, false, false, false,false, false, false, true
});

foo.ToByte();
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
Ali
  • 3,373
  • 5
  • 42
  • 54
0
byte GetByte(BitArray input)
{
  int len = input.Length;
  if (len > 8)
    len = 8;
  int output = 0;
  for (int i = 0; i < len; i++)
    if (input.Get(i))
      output += (1 << (len - 1 - i)); //this part depends on your system (Big/Little)
      //output += (1 << i); //depends on system
  return (byte)output;
}

Cheers!

NothinRandom
  • 225
  • 1
  • 4
  • 12
0

Little endian byte array converter : First bit (indexed with "0") in the BitArray assumed to represents least significant bit (rightmost bit in the bit-octet) which interpreted as "zero" or "one" as binary.

 public static class BitArrayExtender {

    public static byte[] ToByteArray( this BitArray bits ) {

        const int BYTE = 8;
        int length = ( bits.Count / BYTE ) + ( (bits.Count % BYTE == 0) ? 0 : 1 );
        var bytes  = new byte[ length ];

        for ( int i = 0; i < bits.Length; i++ ) {

           int bitIndex  = i % BYTE;
           int byteIndex = i / BYTE;

           int mask = (bits[ i ] ? 1 : 0) << bitIndex;
           bytes[ byteIndex ] |= (byte)mask;

        }//for

        return bytes;

    }//ToByteArray

 }//class
underscore
  • 752
  • 12
  • 9