Newbie question - convert two 8-byte values into a single 16-byte value


#1

Let’s say I have an array of 2 elements of UInt8.
I need to make it into a single element of UInt16.
How?

I thought about doing bit arithmetic (shifting/or’ing) but there’s got to be an easier way?


#2

You reinterpret e.g.

reinterpret(UInt16, UInt8[1,2])


#3

And if your source has a fixed endianness that might be different from the host byte order of your machine, you can use ntoh, hton, ltoh, and htol from base/io.jl to convert. E.g. i use this to read a 16-bit big-endian scalar value from the UInt8 array x:

ntoh(reinterpret(UInt16, x[1:2])[1])

#4

Using arrays to do a little bit of bit manipulation is going to be very inefficient! This is not very hard to write with bit operations:

julia> bitcat(a::UInt8, b::UInt8) = (UInt16(a) << 8) | b
bitcat (generic function with 1 method)

julia> bitcat(0x12, 0x34)
0x1234

julia> @code_native bitcat(0x12, 0x34)
	.section	__TEXT,__text,regular,pure_instructions
; Function bitcat {
; Location: REPL[23]:1
	shll	$8, %edi
	movzbl	%sil, %eax
	orl	%edi, %eax
	retq
	nopw	(%rax,%rax)
;}

#5

Stefan’s remark proven :grin:

julia> bitcat(a::UInt8, b::UInt8) = (UInt16(a) << 8) | b
bitcat (generic function with 1 method)

julia> bitcat(x::Vector{UInt8}) = (UInt16(x[1]) << 8) | x[2]
bitcat (generic function with 2 methods)

Comparing reinterpret, bitcat taking individual arguments, and bitcat taking an array:

julia> @btime reinterpret(UInt16, [0x12, 0x34])
  78.048 ns (3 allocations: 176 bytes)
1-element Array{UInt16,1}:
 0x3412

julia> @btime reinterpret(UInt16, [0x12, 0x34])
  78.280 ns (3 allocations: 176 bytes)
1-element Array{UInt16,1}:
 0x3412

julia> @btime reinterpret(UInt16, [0x12, 0x34])
  77.307 ns (3 allocations: 176 bytes)
1-element Array{UInt16,1}:
 0x3412

julia> @btime bitcat([0x12, 0x34])
  39.327 ns (1 allocation: 96 bytes)
0x1234

julia> @btime bitcat([0x12, 0x34])
  39.276 ns (1 allocation: 96 bytes)
0x1234

julia> @btime bitcat([0x12, 0x34])
  39.401 ns (1 allocation: 96 bytes)
0x1234

julia> @btime bitcat(0x12, 0x34)
  1.760 ns (0 allocations: 0 bytes)
0x1234

julia> @btime bitcat(0x12, 0x34)
  1.759 ns (0 allocations: 0 bytes)
0x1234

julia> @btime bitcat(0x12, 0x34)
  1.760 ns (0 allocations: 0 bytes)
0x1234

#6

Actually I’m surprised that the array version is only 20x slower. If you’re using @btime for timing I think you can just do it once since the whole purpose of that macro is to run something enough times for you automatically – and note that it seems to be working since the measurements of the same operation are all similar.


#7

Do be careful with

@btime bitcat([0x12, 0x34])

Note that

julia> @btime bitcat($([0x12, 0x34]))
  1.791 ns (0 allocations: 0 bytes)
0x1234

and

julia> @btime bitcat(x) setup = x = rand(UInt8, 2)
  1.737 ns (0 allocations: 0 bytes)
0xd87b

See BenchmarkTools docs.