Hello all:

In one of my programs, I had to read data that was saved in binary

format. Some of the data consisted of IEEE 754 single precision floats

saved as 32 bit integers (in network order). My pack/unpack skills are

not that great (I don't think they can handle this case) so I wrote

something to handle the conversion of these numbers.

I would very much appreciate if you can take a look at the code and see

if I am doing anything that I should not be doing or if there is a

better way of doing this.

The function in question is ieee_single_from_int in the string below.

The function is a straight-forward application of the manual steps

needed to go from the integer representation to the floating point

number.

I would like to know if there is an obvious way of doing this that I

have missed or if there is a CPAN module that already handles these

kinds of conversions. If not, I'll package this as a module and start

preparing my first ever CPAN contribution

You can use

http://babbage.cs.qc.edu/IEEE-754/32bit.html to check for

correctness.

http://en.wikipedia.org/wiki/IEEE_754 explains the format.

#!/usr/bin/perl

use strict;

use warnings;

my $buffer;

# The following loop replaces the routine to read reasonably

# sized chunks from the file.

while ( my $line = <DATA> ) {

my $hex;

last unless ( $hex ) = ($line =~ /\A\d{7}: ([[

digit:] ]+)/);

while ( $hex =~ /([[

digit:]]{2})/g ) {

$buffer .= chr( hex $1 );

}

}

for ( my $i = 0; $i < length $buffer; $i += 4 ) {

my $uint32 = unpack 'N', substr( $buffer, $i, 4 );

my ($v, $e) = ieee_single_from_int( $uint32 );

if ( defined $v ) {

printf "%8.8x : % .16f\n", $uint32, $v;

}

else {

warn sprintf "%8.8x : %s\n", $uint32, $e;

}

}

use constant DENOMINATOR => 0x00800000;

use constant UINT32_MASK => 0xffffffff;

use constant SIGN_MASK => 0x80000000;

use constant FRAC_MASK => 0x007fffff;

use constant EXP_MASK => 0x7f800000;

sub ieee_single_from_int {

my $uint32 = ( $_[0] & UINT32_MASK );

my $exp = ( $uint32 & EXP_MASK ) >> 23;

my $frac = $uint32 & FRAC_MASK;

my $sign = $uint32 & SIGN_MASK ;

my ($v, $e);

if ( $exp and $exp < 0xff ) {

$v = ( 1 + $frac / DENOMINATOR ) * ( 2**( $exp - 127) );

}

elsif( $exp == 0x00 ) {

$v = ( $frac / DENOMINATOR ) * ( 2**( -126 ) );

}

elsif( $exp == 0xff ) {

$e = $frac ? "NaN"

: $sign ? "-Infinity"

: "+Infinity";

}

$v = -$v if defined( $v ) and $sign;

return wantarray ? ( $v, $e ) : $v;

}

__DATA__

0000420: 4016 2933 3f1b 739a be86 8200 c00d c853 @.)3?.s........S

0000430: bf18 7633 404a 3eba bfc5 b34d 3ea3 00a7 ..v3@J>....M>...

0000440: bfae 1e10 3e30 8d00 bfa0 02da bfb9 2bed ....>0........+.

0000450: 3f33 66da bfbc 9b4d 3fa3 c200 c088 cd93 ?3f....M?.......

0000460: 40f2 5e4a 4005 5407 c086 b92a bf61 5f8a @.^J@.T....*.a_.

0000470: bf2a 75da 3f5d 2a4d bf9a 1373 bfbd 475a .*u.?]*M...s..GZ

Thank you for your time.

Sinan