Bitstring generator gives odd result for 6 and 7 bits

I understand these results:

iex(1)> for <<b::1 <- <<0xff::16>> >>, do: b
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
iex(2)> for <<b::2 <- <<0xff::16>> >>, do: b
[0, 0, 0, 0, 3, 3, 3, 3]
iex(3)> for <<b::3 <- <<0xff::16>> >>, do: b
[0, 0, 1, 7, 7]
iex(4)> for <<b::4 <- <<0xff::16>> >>, do: b
[0, 0, 15, 15]
iex(5)> for <<b::5 <- <<0xff::16>> >>, do: b
[0, 3, 31]

but here I’d expect [3, 63]

iex(6)> for <<b::6 <- <<0xff::16>> >>, do: b
[0, 15]

… and [1, 127] here

iex(7)> for <<b::7 <- <<0xff::16>> >>, do: b
[0, 63]

this makes sense again

iex(8)> for <<b::8 <- <<0xff::16>> >>, do: b
[0, 255]

[0, 15] is to be expected:

0xff::16 maps to [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]. Grouping this int groups of 6 bits will give you:

  • [0, 0, 0, 0, 0, 0], which equals 0
  • [0, 0, 1, 1, 1, 1], which equals 15 and
  • [1, 1, 1, 1], which is too small and discarded.
3 Likes

OK, so the bitstring taken from should (normally) have a size with a multiple of the target size.

sth like ceil(bit_size(data) / target_bitsize) * target_bitsize

iex(30)> for <<b::7 <- <<0xff::14>> >>, do: b
[1, 127]
iex(31)> for <<b::6 <- <<0xff::12>> >>, do: b
[3, 63]

Here is a code example which produces data that you expect:

defmodule Example do
  def sample(bitstring, size) do
    # simply calculate all sizes
    bit_size = bit_size(bitstring)
    extra_size = rem(bit_size, size)
    target_size = bit_size - extra_size
    # using pattern matching and above variables
    # we are ignoring x extra 0 bits at start of bitstring
    # where x here is calculated as extra_size
    <<0::size(extra_size), target::size(target_size)>> = bitstring
    # original loop but more generic and with target data
    for <<(item::size(size) <- <<target::size(target_size)>>)>>, do: item
  end
end

iex> Example.sample(<<0xFF::16>>, 1)
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
iex> Example.sample(<<0xFF::16>>, 2)
[0, 0, 0, 0, 3, 3, 3, 3]
iex> Example.sample(<<0xFF::16>>, 3)
[0, 0, 3, 7, 7]
iex> Example.sample(<<0xFF::16>>, 4)
[0, 0, 15, 15]
iex> Example.sample(<<0xFF::16>>, 5)
[0, 7, 31]
iex> Example.sample(<<0xFF::16>>, 6)
[3, 63]
iex> Example.sample(<<0xFF::16>>, 7)
[1, 127]
iex> Example.sample(<<0xFF::16>>, 8)
[0, 255]

Note: I’m not sure what you are trying to do with it, so you may want to also use a case condition when extra bits are not 0, for example <<0xFFFF::16>>.

Helpful resources:

  1. [Guard] bit_size/1 - Returns an integer which is the size in bits of bitstring.
  2. [Guard] rem/2 - Computes the remainder of an integer division.
  3. [Special form] <<>>/1 - Defines a new bitstring.
1 Like