As shown already, this can be done by ImportByteArray
. If you have long lists, this can be slow. Here is a test.
SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
First[Timing[
Map[ImportByteArray[ByteArray[#], "Real32", ByteOrdering -> -1] &,
bytes]
]],
{n, 2, 12}
]
(* Out[302]= {0.004421, 0.007021, 0.013251, 0.025017, 0.045063, \
0.084426, 0.172539, 0.333143, 0.673713, 1.36853, 2.72413} *)
One can instead do the conversion the pedestrian way, using the bit fields of a single precision 32-bit real. First is the sign bit, next eight give the exponent between $2^{-127}$ and $2^{127}-1$. There is an implied first bit set to 1 in the mantissa, and the remaining mantissa bits are filled in by our byte array bits.
elen = 8;
SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Timing[Length[Map[(
bits = Join @@ IntegerDigits[Reverse@#, 2, 8];
sign = (-1)^First[bits];
{exponbits, mantissabits} = TakeDrop[Rest[bits], elen];
mantissabits = Prepend[mantissabits, 1];
expon = FromDigits[exponbits, 2] - 2^(elen - 1) + 2;
mantissa = FromDigits[mantissabits, 2]*2^(-Length[mantissabits]);
N[sign*2^expon*mantissa]) &, bytes]]
],
{n, 2, 12}
]
(* Out[329]= {{0.000095, 1}, {0.000037, 2}, {0.000069, 4}, {0.000142,
8}, {0.000234, 16}, {0.000558, 32}, {0.001268, 64}, {0.003829,
128}, {0.005531, 256}, {0.008505, 512}, {0.016775, 1024}} *)
That's two orders of magnitude. Oh, and they do give the same results.
In[332]:= SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Map[ImportByteArray[ByteArray[#], "Real32",
ByteOrdering -> -1][[1]] &, bytes],
{n, 2, 4}
]
(* Out[333]= {{1.76353*10^-34}, {3.52751*10^15, -4230.}, {-0.035666,
4.6259*10^23, 0.000014599, -6.77262*10^35}} *)
In[334]:= SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Map[(
bits = Join @@ IntegerDigits[Reverse@#, 2, 8];
sign = (-1)^First[bits];
{exponbits, mantissabits} = TakeDrop[Rest[bits], elen];
mantissabits = Prepend[mantissabits, 1];
expon = FromDigits[exponbits, 2] - 2^(elen - 1) + 2;
mantissa = FromDigits[mantissabits, 2]*2^(-Length[mantissabits]);
N[sign*2^expon*mantissa]) &, bytes],
{n, 2, 4}
]
(* Out[335]= {{1.76353*10^-34}, {3.52751*10^15, -4230.}, {-0.035666,
4.6259*10^23, 0.000014599, -6.77262*10^35}} *)
--- edit ---
As note in a response by @Roman, there are faster ways, for example using a link to C or similar and casting as a 32 bit machine real. I do not have that level of casting ability (I'd need to rise above assistant assistant producer). But the bit fiddling approach can be sped up. First the reference implementation.
toReal32a[bytes_, elen_ : 8] := Module[
{bits, sign, exponbits, mantissabits, expon, mantissa},
bits = Join @@ IntegerDigits[Reverse@bytes, 2, 8];
sign = (-1)^First[bits];
{exponbits, mantissabits} = TakeDrop[Rest[bits], elen];
mantissabits = Prepend[mantissabits, 1];
expon = FromDigits[exponbits, 2] - 2^(elen - 1) + 2;
mantissa = FromDigits[mantissabits, 2]*2^(-Length[mantissabits]);
N[sign*2^expon*mantissa]
]
First redo using bitwise operations. This will not give a speed gain.
toReal32b[bytes_, elen_ : 8] := Module[
{uint, signmask = 2^31, offset = 31 - elen, emask, mmask, sign,
expon, mantissa},
emask = 2^31 - 2^offset;
mmask = 2^offset - 1;
uint = 2^Range[0, 24, 8] . bytes;
sign = If[BitAnd[uint, signmask] == 0, 1, -1];
expon =
BitShiftRight[BitAnd[uint, emask], offset] - 2^(elen - 1) + 2;
mantissa = BitOr[2^offset, BitAnd[uint, mmask]];
N[sign*2^expon*2^(-offset - 1)*mantissa]
]
This version is easy to run through Compile
. (Maybe so is the first version, I just doubt it will be quite as fast.)
toReal32c = Compile[{{bytes, _Integer, 1}, {elen, _Integer}}, Module[
{uint, signmask = 2^31, offset = 31 - elen, emask, mmask, sign,
expon, mantissa},
emask = 2^31 - 2^offset;
mmask = 2^offset - 1;
uint = 2^Range[0, 24, 8] . bytes;
sign = If[BitAnd[uint, signmask] == 0, 1, -1];
expon =
BitShiftRight[BitAnd[uint, emask], offset] - 2^(elen - 1) + 2;
mantissa = BitOr[2^offset, BitAnd[uint, mmask]];
sign*2.^(expon - offset - 1)*mantissa
], CompilationTarget -> "C", RuntimeOptions -> "Speed"];
Check that they agree.
In[36]:= SeedRandom[1234];
bytes = RandomInteger[{0, 255}, 4]
Map[#[bytes, 8] &, {toReal32a, toReal32b, toReal32c}]
(* Out[37]= {224, 105, 106, 7}
Out[38]= {1.76353*10^-34, 1.76353*10^-34, 1.76353*10^-34} *)
This last delivers far better timings than the reference.
In[39]:= SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Timing[Length[Map[toReal32a, bytes]]], {n, 2, 12}
]
Out[40]= {{0.000112, 1}, {0.000067, 2}, {0.000144, 4}, {0.000226,
8}, {0.000448, 16}, {0.001035, 32}, {0.001595, 64}, {0.003396,
128}, {0.006998, 256}, {0.014437, 512}, {0.028745, 1024}}
In[41]:= SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Timing[Length[Map[toReal32b, bytes]]], {n, 2, 12}
]
Out[42]= {{0.000149, 1}, {0.000098, 2}, {0.000202, 4}, {0.000424,
8}, {0.000737, 16}, {0.001521, 32}, {0.00296, 64}, {0.005906,
128}, {0.012203, 256}, {0.020636, 512}, {0.02759, 1024}}
In[43]:= SeedRandom[1234];
Table[
bytes = Partition[RandomInteger[{0, 255}, 2^n], 4];
Timing[Length[Map[toReal32c[#, 8] &, bytes]]], {n, 2, 12}
]
Out[44]= {{0.000034, 1}, {9.*10^-6, 2}, {0.00001, 4}, {0.000016,
8}, {0.000027, 16}, {0.000054, 32}, {0.0001, 64}, {0.000226,
128}, {0.00013, 256}, {0.000254, 512}, {0.000502, 1024}}
--- end edit ---
ImportByteArray[ByteArray[{189,178,61,188},"Real32"]
gives the little-endian decoding of -0.01157826. $\endgroup$