Koray Tugay
Koray Tugay

Reputation: 23800

What does bitwise-anding a byte with 0b1111_1111 do?

I have the following method:

int convert3ByteChunkTo24Bits(final byte[] bytes) {
    int bitsFor3ByteChunk = 0;

    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0] & 0b11111111);

    bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[1] & 0b11111111);

    bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[2] & 0b11111111);

    return bitsFor3ByteChunk & 0b00000000_11111111_11111111_11111111;
}

Purpose of the method is to append bytes passed and return it in an int format. Here is a test method I have:

void testConvert3ByteChunkTo24Bits() {
    final byte ca = (byte) 0xCA;
    final byte fe = (byte) 0xFE;
    final byte ba = (byte) 0xBA;

    final byte[] man = {ca, fe, ba};
    final int bytesIn24Bits = base64EncoderHelper.convert3ByteChunkTo24Bits(man);

    System.out.println(bytesIn24Bits);
}

Output of the result will be:

13303482

which in binary is:

00000000110010101111111010111010

This is correct and is the intended results since:

0xCA = 11001010
0xFE = 11111110
0xBA = 10111010

So, so far so good. There is just one thing I do not understand, why do I need the logical ands as in

(bytes[0] & 0b11111111)

When I do not do the bitwise-and operations, so if the implementation is as follows:

int convert3ByteChunkTo24Bits(final byte[] bytes) {
    int bitsFor3ByteChunk = 0;

    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0]);

    bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[1]);

    bitsFor3ByteChunk = bitsFor3ByteChunk << 8;
    bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[2]);

    return bitsFor3ByteChunk & 0b00000000_11111111_11111111_11111111;
}

result will be:

00000000111111111111111110111010

What am I missing? What does

bytes[0] & 0b11111111

fix here? (I found it kind of by trial - error and still curious why it works..)

Upvotes: 1

Views: 893

Answers (3)

Marnes
Marnes

Reputation: 661

The human-readable equivalent is simply

Byte.toUnsignedInt(b)

or if you want it really explicitly but inefficiently:

b < 0?  b + 256: b

Honestly, writing code like that when jdk methods or simple math exist to do the same thing is just obfuscation and I hope people who write code like that don't ever end up in a team with other people... Unless they're writing a device driver in C or some other inherently low-level byte-wrangling project like that.

Upvotes: 0

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36431

In:

bitsFor3ByteChunk = bitsFor3ByteChunk | (bytes[0] & 0b11111111);

bytes[0] is first extended to int value (integers are all represented in two's-complement). Then for a value like 0xFF stored in a byte, the corresponding int value will be 0xFFFFFFFF. It is then masked to retain the lower 8 bits.

Upvotes: 1

Malt
Malt

Reputation: 30335

It handles signed bytes.

say byte[0] has a value of 0b10000000. That's looks like 128 to you and me, but to Java it looks like -128, due to two's complement arithmatic. When you use that value in a calculation with an int, it'll be sign-extended to an int with a value of 11111111111111111111111110000000, which is an int with a value of -128

To avoid the problem, this code performs a bitwise and with 0b11111111. This is an integer operation, so the byte is implicitly cast to the int 11111111111111111111111110000000, but the and turns it into 00000000000000000000000010000000, which is the desired 128.

This is a hack for the notorious Java problem of not having unsigned types.

Upvotes: 1

Related Questions