Extracting 15-bit integer values from binary with pattern matching

I have time deltas stored in a file in two formats:
0xxxxxxx
1yyyyyyyyyyyyyyy

In the first format, the time delta is the unsigned 7 bit field represented by xxxxxxx. In the second format, the time delta is the signed 15 bit field represented by yyyyyyyyyyyyyyy

pattern matching to extract the values works for the 7 bit unsigned case:

iex(12)> <<0::1, num::size(7)-unsigned-integer-little>> = <<2>>; num
2

But for the intuitive extension to the 15-bit signed case, it isn’t so obvious that it is working correctly:

iex(15)> <<1::1, num::size(15)-signed-integer-little>> = <<255,255>>; num
-1

That’s reasonable. However:

iex(14)> <<1::1, num::size(15)-signed-integer-little>> = <<254,255>>; num
-3

The value of the 15 bit signed field is -2, not -3.

I’ve been searching for the documentation fruitlessly, so a pointer to the place where I can read the fine documentation, or else just the answer, would be appreciated.

thanks…

http://erlang.org/doc/programming_examples/bit_syntax.html

I have just recently started using bitstrings but I’m trying to debug this with you :slight_smile:
In the meantime I link the above docs for you and others.

(I’m no expert in this)

Looks like this goes per 8 bits so:

iex(225)> <<_::1, number::15-integer-little-signed>> = <<1::1, 254::8, 127::7>>
<<255, 127>>
iex(226)> number
-2

So: first bit is ignored by you, then 8 bits with 2^7…2^0, then sign::1, then 6 bit 2^13…2^8.
So last chunk is 7 bits with first bit as sign.

Please correct me if I’m wrong, I just figured this out with iex.
Try to debug with this:

<<_::1, number::15-integer-little-signed>> = <<1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1, 1::1>> 

Edit:
So just add the ignored bit to the beginning, and reduce the size of the last integer by 1 bit.

<<1::1, 255::8, 127::7>>

Thank you for the pointer to the erlang documentation. That looks like the syntax, with the : (erlang) vs. :: (elixir) difference.

Looking at that documentation inspired me to ask what elixir would construct, given the same layout as in the pattern, so I entered the following into iex. It seems that the affected bits are the least significant bits of the first byte and the most significant bits of the second byte.

Without a specification of how to place a 1 bit field and a 15-bit little endian field into two bytes, I can’t say I know how to write the elixir pattern,

I’m going to consider this as closed. (Not sure how to/if I can close a topic in this forum.) Thank you for your insights.

iex(57)> Enum.map(-16..16, &(<<0::1, &1::15-signed-little>> <> <<0>> <> <<&1::8>>))
[
  <<120, 127, 0, 240>>,
  <<120, 255, 0, 241>>,
  <<121, 127, 0, 242>>,
  <<121, 255, 0, 243>>,
  <<122, 127, 0, 244>>,
  <<122, 255, 0, 245>>,
  <<123, 127, 0, 246>>,
  <<123, 255, 0, 247>>,
  <<124, 127, 0, 248>>,
  <<124, 255, 0, 249>>,
  <<125, 127, 0, 250>>,
  <<125, 255, 0, 251>>,
  <<126, 127, 0, 252>>,
  <<126, 255, 0, 253>>,
  <<127, 127, 0, 254>>,
  <<127, 255, 0, 255>>,
  <<0, 0, 0, 0>>,
  <<0, 128, 0, 1>>,
  <<1, 0, 0, 2>>,
  <<1, 128, 0, 3>>,
  <<2, 0, 0, 4>>,
  <<2, 128, 0, 5>>,
  <<3, 0, 0, 6>>,
  <<3, 128, 0, 7>>,
  <<4, 0, 0, 8>>,
  <<4, 128, 0, 9>>,
  <<5, 0, 0, 10>>,
  <<5, 128, 0, 11>>,
  <<6, 0, 0, 12>>,
  <<6, 128, 0, 13>>,
  <<7, 0, 0, 14>>,
  <<7, 128, 0, 15>>,
  <<8, 0, 0, 16>>
]
1 Like