Jared Beck
Jared Beck

Reputation: 17538

Combine four 8-bit unsigned ints into one 32-bit unsigned int

Given an array of four 8-bit unsigned ints, for example a 32-bit RGBA color

rgba = [255, 255, 255, 255] # white

what is the most efficient way to combine these four Fixnums into a single 32-bit Integer?

Currently, I'm using pack and unpack:

rgba.pack('C*').unpack('L')[0] #=> 4294967295

# Check work: same value as corresponding hex literal
0xffffffff #=> 4294967295

but I wonder if there's a more efficient way. I found this C++ question:

Cleanest way to combine two shorts to an int

and I think << is a "shift" and | is a "mask", but I don't know enough about those operators, and have never used them in ruby.

Upvotes: 4

Views: 3958

Answers (1)

August
August

Reputation: 12568

Shift each element by 24 - (index * 8), then OR them all together:

rgba = [255, 255, 255, 255]
rgba[0] << 24 | rgba[1] << 16 | rgba[2] << 8 | rgba[3]

I didn't shift rgba[3] because shifting by zero has no effect.

Some measurements:

Using unpack

Benchmark.measure do
  100_000.times do
    rgba = [255, 255, 255, 255]
    rgba.pack('C*').unpack('L')[0]
  end
end

Result:

=> #<Benchmark::Tms:0x007fe137005350
 @cstime=0.0,
 @cutime=0.0,
 @label="",
 @real=0.146899,
 @stime=0.0,
 @total=0.1399999999999999,
 @utime=0.1399999999999999>

Using bitwise operators

Benchmark.measure do
  100_100.times do
    rgba = [255, 255, 255, 255]
    rgba[0] << 24 | rgba[1] << 16 | rgba[2] << 8 | rgba[3]
  end
end

Result:

=> #<Benchmark::Tms:0x007fd66438fd28
 @cstime=0.0,
 @cutime=0.0,
 @label="",
 @real=0.088995,
 @stime=0.0,
 @total=0.08000000000000007,
 @utime=0.08000000000000007>

Upvotes: 3

Related Questions