Oddly enough I had almost exactly the same problem a
couple of days ago. I kept feeling that there must be
a way to combine pack and unpack but
I couldn't get anything to work.
In the end I decided to get back to fundimentals, and work
out what the bits looked like
# This is from memory so may not work
$binary_struct = read_from_somewhere();
$was_bits = unpack("b64",$binary_struct);
$now_bits = shuffle_bits($was_bits);
$float_val = unpack("d",pack("b64",$now_bits));
This is not particularly elegant but it does let you play
with different combinations of reversing bytes, whole
sequences or doing other strange combinations.
In the debugger try $v = unpack("b64",2.5)
(and a few other values) to see what your machine does
and compare that with what you get from the file.
This is the actual routine I used (you input data
is probably not in the same byte order so you will
have to play with the $binary2double_fun a bit.
sub deduce_endian
{
my($v);
# Deduce the machine's native FP format by encoding the
# number 2.5 as a bit string. Once we know how to get
# from doubles to bit strings we can deduce the function
# for doing the reverse from our binary data
$v = unpack("b64",pack("d",2.5));
if($v eq
"0000000000000000000000000000000000000000000000000010000000000010"
)
{
# Intel or something like it
$endian = "ieee-little";
$binary2double_fun = sub
{
# We have to reverse the bitstring
my $bits = reverse(shift);
# Then map to floats
return(unpack("d",pack("b64",$bits)));
};
}
elsif($v eq
"0000001000100000000000000000000000000000000000000000000000000000"
)
{
# *NIX hardware
$endian = "ieee-big";
$binary2double_fun = sub
{
# a combination of pack and unpack will do it
my $bits = shift;
my $res;
while($bits)
{
$res .= reverse(substr($bits,0,8));
$bits = substr($bits,8);
}
return(unpack("d",pack("b64",$res)));
};
}
else
{
print STDERR "Not configured for this machine yet\n";
exit(20);
}
}
# Then I called it like this
# Read from the file
$out = hex2binary($in);
# Then map this to our double representation
deduce_endian() if(!$endian);
return &{$binary2double_fun}($out) if($binary2double_fun);
The only difference I had (as you can see) is that I was
using 8 byte floats rather than 4 (IEEE format). I am
sure that there is a better way to do this but I just
wanted to get it working.
|