Conversion from uint8 to int8 (x 24)

Daniel Baulig, a co-worker at Facebook, told me a little trick related to jDataView function to convert from a uint8 to a int8 in Javascript.

Here’s the version I had:

function getInt8() {
  var b = this.getUint8();
  if (b > Math.pow(2, 7) - 1) {
    return b - Math.pow(2, 8);
  }
  return b;
}

Compare it to his version:

< <function getInt8() {
  return this.getUint8() << 24 >> 24;
}

I was really confused because it seems like it’s doing a no-op. Here’s the full explanation of why the two versions are working.

How it works?

The following table (borrowed from Wikipedia) shows how various 8 bits values are in represented with bits and how they are interpreted in unsigned and signed (using two-complement rule).

.uint8table {margin: 0 auto; border-collapse: collapse; }
.uint8table td, .uint8table th { padding: 2px 5px; border: 1px solid #ccc; text-align: center; }
.uint8table b { background-color: #FFE0ED; font-weight: normal; }
.uint8table i { color: purple; font-style: normal; }
.uint8table .sep td { border-bottom: 2px solid #bbb; }
#hidefirsttwo span:nth-child(1), #hidefirsttwo span:nth-child(2) { display: none; }
#hidefirsttwo span:nth-child(3) { margin-left: -7px; }

Bits uint8 int8
0000 0000 0 0
0000 0001 1 1
0000 0010 2 2
0111 1110 126 126
0111 1111 127 127
1000 0000 128 −128
1000 0001 129 −127
1000 0010 130 −126
1111 1110 254 −2
1111 1111 255 −1

Javascript doesn’t natively have a 8 bit integer type, it only has a 32 bits one. When you put a 8 bit integer into a 32 bits one, Javascript is going to fill the remaining bits on the left with zeros as the following table shows.

Bits int32
0000 0000 … 0000 0000 0
0000 0000 … 0000 0001 1
0000 0000 … 0000 0010 2
0000 0000 … 0111 1110 126
0000 0000 … 0111 1111 127
0000 0000 … 1000 0000 128
0000 0000 … 1000 0001 129
0000 0000 … 1000 0010 130
0000 0000 … 1111 1110 254
0000 0000 … 1111 1111 255

Unfortunately, this doesn’t properly handle negative numbers. Because we use two-complement, we’ve got to fill all the bits with 1 for negative numbers in order to have the same number in a signed 32 bits representation.

Bits int32
0000 0000 … 0000 0000 0
0000 0000 … 0000 0001 1
0000 0000 … 0000 0010 2
0000 0000 … 0111 1110 126
0000 0000 … 0111 1111 127
1111 1111 … 1000 0000 −128
1111 1111 … 1000 0001 −127
1111 1111 … 1000 0010 −126
1111 1111 … 1111 1110 −2
1111 1111 … 1111 1111 −1

So basically, we’ve got to fill the 24 remaining bits on the left with the same first bit we have: 0 for positive numbers and 1 for negative numbers.

This is when the trick comes into place. In javascript, there’s a binary operator: >> >_(Sign-propagating_right_shift)”>Sign-propagating right shift that moves all the bits to the right and fills the missing bits with the first bit.

So all we have to do is to put our 8 good digits to the far left using << and then use the previous trick to fill the bits with the proper ones 🙂

x x < < 24 (x <> 24
0000 0000 … 0000 0000 0000 0000 … 0000 0000 0000 0000 … 0000 0000
0000 0000 … 0000 0001 0000 0001 … 0000 0000 0000 0000 … 0000 0001
0000 0000 … 0000 0010 0000 0010 … 0000 0000 0000 0000 … 0000 0010
0000 0000 … 0111 1110 0111 1110 … 0000 0000 0000 0000 … 0111 1110
0000 0000 … 0111 1111 0111 1111 … 0000 0000 0000 0000 … 0111 1111
0000 0000 … 1000 0000 1000 0000 … 0000 0000 1111 1111 … 1000 0000
0000 0000 … 1000 0001 1000 0001 … 0000 0000 1111 1111 … 1000 0001
0000 0000 … 1000 0010 1000 0010 … 0000 0000 1111 1111 … 1000 0010
0000 0000 … 1111 1110 1111 1110 … 0000 0000 1111 1111 … 1111 1110
0000 0000 … 1111 1111 1111 1111 … 0000 0000 1111 1111 … 1111 1111
If you liked this article, you might be interested in my X feed as well.