Convert integer to bits array

I’m trying to find a way to convert an unsigned integer value into a bitmask.

Does anyone know of a way to do this efficiently using the standard library?

For example, I want something to do the following:

input = UInt16(5)
println(bitmask(input))

and get [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1] back.

bitstring(UInt16(5)), will return a string like “000000…101”, which you can then split and convert to a bitvector or array of ints.

Not very efficient of course. I’m sure someone can offer a better option.

1 Like

You can & with 1 and repeatedly right shift.

2 Likes

digits(x, base=2, pad=16) |> reverse

12 Likes

I need this occasionally and always come up with an ad-hoc solution, I wonder if a function in Base would make sense.

2 Likes

Thanks everyone! The digits call is the fastest by far btw.


z = UInt(5)
@time digits(z, base=2, pad=16)
  0.000030 seconds (11 allocations: 640 bytes)
@time bitstring(UInt16(5))
  0.035250 seconds (47.15 k allocations: 2.489 MiB)

This is after running each function one time to trigger jit compilation btw.

Edit:

joemiller is correct! I pasted the values with compilation for bitstring

@time bitstring(z)
  0.000007 seconds (6 allocations: 320 bytes)
"0000000000000000000000000000000000000000000000000000000000000101"

@time digits(z, base=2, pad=16)
  0.000023 seconds (11 allocations: 640 bytes)

The bitstring method is faster now that I’m doing it correctly.

2 Likes

The proper way is

julia> function bm(u)
       res =BitVector(undef, sizeof(u)*8)
       res.chunks[1] = u%UInt64
       res
       end
julia> bm(0x05)
8-element BitArray{1}:
  true
 false
  true
 false
 false
 false
 false
 false

This is not exactly what you asked for: The bit pattern is reversed (little endian bitorder). You can use this code to convert to big endian (better than the llvm bitreverse intrinsic):

julia> function revbits(z::UInt8)
                  z = (((z & 0xaa) >>  1) | ((z & 0x55) <<  1))
                  z = (((z & 0xcc) >>  2) | ((z & 0x33) <<  2))
                  z = (((z & 0xf0) >>  4) | ((z & 0x0f) <<  4))
                  return z
              end

julia> function revbits(z::UInt16)
                  z = (((z & 0xaaaa) >>  1) | ((z & 0x5555) <<  1))
                  z = (((z & 0xcccc) >>  2) | ((z & 0x3333) <<  2))
                  z = (((z & 0xf0f0) >>  4) | ((z & 0x0f0f) <<  4))
                  return ntoh(z)
              end

julia> function revbits(z::UInt32)
                  z = (((z & 0xaaaaaaaa) >>  1) | ((z & 0x55555555) <<  1))
                  z = (((z & 0xcccccccc) >>  2) | ((z & 0x33333333) <<  2))
                  z = (((z & 0xf0f0f0f0) >>  4) | ((z & 0x0f0f0f0f) <<  4))
                  return ntoh(z)
              end

julia> function revbits(z::UInt64)
                  z = (((z & 0xaaaaaaaaaaaaaaaa) >>  1) | ((z & 0x5555555555555555) <<  1))
                  z = (((z & 0xcccccccccccccccc) >>  2) | ((z & 0x3333333333333333) <<  2))
                  z = (((z & 0xf0f0f0f0f0f0f0f0) >>  4) | ((z & 0x0f0f0f0f0f0f0f0f) <<  4))
                  return ntoh(z)
              end

and use

julia> function revbm(u)
       res =BitVector(undef, sizeof(u)*8)
       res.chunks[1] = revbits(unsigned(u))%UInt64
       res
       end
julia> revbm(Int8(5))
8-element BitArray{1}:
 false
 false
 false
 false
 false
  true
 false
  true
4 Likes

The allocations and time on @time bitstring(UInt16(5)) seem wrong, it looks like it’s compiling.

Sorry about that! You are correct. I updated my timings.

1 Like