Binary to integer (no parsing)

Hi,

Is there a function that would convert <<1>> to 1, <<1, 0>> to 256. I do not mean parsing the binary as a string representation of the integer.

I want to know how bing a 20 bytes integer can be.

b = 1..20 |> Enum.map(fn _ -> 255 end) |> :erlang.list_to_binary
<<255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255>>

Thank you.

1 Like

If you’re just doing one number, you can use pattern matching likr this:

<<number::16>> = <<1, 0>>

And that will give you 256. This strategy will work for a 20 byte integer (obviously not with 16). Though if you’re using it as a literal I recommend number=0xFFFFFFFF....FFFFF

1 Like
iex(2)> <<int::integer-size(2)-unit(8)>> = <<255, 255>>
<<255, 255>>
iex(3)> int
65535
3 Likes

Not builtin, you have to implement that on your own. Though thats not an easy task, as there are meany questions you didn’t even ask, like endianess or signedness.

20 bytes are 160 bits. Setting them all to 1 is 2^160 - 1 in unsigned interpretation.

Actually I wanted to know the number of possible values, sorry for the XY question. But I am still interested in writing such function.

Say unsigned big endian. I came up with that. It seems to work, but I find it unsatisfying:


expected = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
^expected = 1_461_501_637_330_902_918_203_684_832_716_283_019_655_932_542_975

defmodule T do
  def to_int(bin, endianness \\ :big) do
    list =
      bin
      |> :erlang.binary_to_list()

    case endianness do
      :big -> :lists.reverse(list)
      :little -> list
    end
    |> Enum.with_index()
    |> Enum.reduce(0, fn {byte, i}, acc ->
      acc + byte * round(:math.pow(256, i))
    end)
  end
end

256 = T.to_int(<<1, 0>>)

^expected =
  T.to_int(
    <<255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
      255, 255>>
  )

System.halt()

(edit: I guess with signed ints we could abuse the binary_to_term function by adding <<131,98>> in front and making sure the binary length is a multiple of 4 bytes …)

There are 2^n possible values in n bits.

3 Likes
fn bin ->
  size = :erlang.size(bin) * 8
  <<n::size(size)>> = bin
  n
end

If you want to play with endianness and what not there are binary pattern matching options you can shove into that match.

Try :binary.decode_unsigned/1,2, it does exactly what your code does…

8 Likes

that function is kind of overcomplicated, consider (untested):

def to_int (<<>>, acc \\ 0), do: acc
def to_int (<<byte, rest::binary>>, acc \\ 0), do: to_int(rest, acc * 256 + byte)

little-endian left as an exercise… :wink:

Thank you all :slight_smile: