I have an array of interleaved signed 24-bit ints (complex numbers) in little endian order that I would like to convert to a complex array of floats or doubles. By interleaved, I mean:
R1 R2 R3 I1 I2 I3 R4 R5 R6 I4 I5 I6 . . .
Where each item is an 8-bit byte, and each three together are a 24-bit int, with R = real and I = imaginary.
What's the most efficient way to do this in C#? The code has to run many times, so I'm trying to squeeze every last cycle out of it I can. I'm hoping for something more efficient than a brute force shift, or and cast.
I wouldn't mind using unsafe code in this case, if it would help.
Here's the baseline, brute-force approach with the second number of the pair commented out, and with sign handling ignored for the moment, to simplify the IDL:
class Program
{
    const int Size = 10000000;
    static void Main(string[] args)
    {
        //
        // Array of little-endian 24-bit complex ints
        // (least significant byte first)
        //
        byte[] buf = new byte[3 * 2 * Size];
        float[] real = new float[Size];
        //float[] imag = new float[Size];
        //
        // The brute-force way
        //
        int j = 0;
        Stopwatch timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < Size; i++)
        {
            real[i] = (float)(buf[j] | (buf[j + 1] << 8) | (buf[j + 2] << 16));
            j += 3;
            // imag[i] = (float)(buf[j] | (buf[j + 1] << 8) | (buf[j + 2] << 16));
            j += 3;
        }
        timer.Stop();
        Console.WriteLine("result = " + 
            (float)(timer.ElapsedMilliseconds * 1000.0f / Size) + 
            " microseconds per complex number");
        Console.ReadLine();
    }
}
and the associated IDL:
  IL_0024:  ldc.i4.0
  IL_0025:  stloc.s    i
  IL_0027:  br.s       IL_0050
  IL_0029:  ldloc.1
  IL_002a:  ldloc.s    i
  IL_002c:  ldloc.0
  IL_002d:  ldloc.2
  IL_002e:  ldelem.u1
  IL_002f:  ldloc.0
  IL_0030:  ldloc.2
  IL_0031:  ldc.i4.1
  IL_0032:  add
  IL_0033:  ldelem.u1
  IL_0034:  ldc.i4.8
  IL_0035:  shl
  IL_0036:  or
  IL_0037:  ldloc.0
  IL_0038:  ldloc.2
  IL_0039:  ldc.i4.2
  IL_003a:  add
  IL_003b:  ldelem.u1
  IL_003c:  ldc.i4.s   16
  IL_003e:  shl
  IL_003f:  or
  IL_0040:  conv.r4
  IL_0041:  stelem.r4
  IL_0042:  ldloc.2
  IL_0043:  ldc.i4.3
  IL_0044:  add
  IL_0045:  stloc.2
  IL_0046:  ldloc.2
  IL_0047:  ldc.i4.3
  IL_0048:  add
  IL_0049:  stloc.2
  IL_004a:  ldloc.s    i
  IL_004c:  ldc.i4.1
  IL_004d:  add
  IL_004e:  stloc.s    i
  IL_0050:  ldloc.s    i
  IL_0052:  ldc.i4     0x989680
  IL_0057:  blt.s      IL_0029
 
     
    