I have a project where a function receives four 8-bit characters and needs to convert the resulting 32-bit IEEE-754 float to a regular Perl number. It seems like there should be a faster way than the working code below, but I have not been able to figure out a simpler pack function that works.
It does not work, but it seems like it is close:
$float = unpack('f', pack('C4', @array[0..3]); # Fails for small numbers
Works:
@bits0 = split('', unpack('B8', pack('C', shift))); @bits1 = split('', unpack('B8', pack('C', shift))); @bits2 = split('', unpack('B8', pack('C', shift))); @bits3 = split('', unpack('B8', pack('C', shift))); push @bits, @bits3, @bits2, @bits1, @bits0; $mantbit = shift(@bits); $mantsign = $mantbit ? -1 : 1; $exp = ord(pack('B8', join('',@bits[0..7]))); splice(@bits, 0, 8); # Convert fractional float to decimal for (my $i = 0; $i < 23; $i++) { $f = $bits[$i] * 2 ** (-1 * ($i + 1)); $mant += $f; } $float = $mantsign * (1 + $mant) * (2 ** ($exp - 127));
Anyone have a better way?
I’d take the opposite approach: forget unpacking, stick to bit twiddling.
First, assemble your 32 bit word. Depending on endianness, this might have to be the other way around:
Now extract the parts of the word: the sign bit, exponent and mantissa:
Assemble your float:
There’s some examples on Wikipedia.