Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Converting Hex to Float (and vise versa)

by unstable_geek (Acolyte)
on Feb 06, 2004 at 18:03 UTC ( #327171=perlquestion: print w/ replies, xml ) Need Help??
unstable_geek has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks. I have found many references that describe how to convert dec<->hex, but I have a slightly different question. I have some 4 byte IEEE floats (in various byte orders) coming to me in ASCII (e.g. 0x428C0000) that I need to convert to (or at least print as) a float. I'm sure the answer involves pack() but I can't figure it out.
I hate my sig

Comment on Converting Hex to Float (and vise versa)
Re: Converting Hex to Float (and vise versa)
by hawtin (Prior) on Feb 06, 2004 at 19:10 UTC

    It's even more complex than that, you have to take into account the architecture of the CPU as well. When I did this I found it most usefull to start from floats and get to the hex. I also did it via binary:

    $f2_5 = unpack("b64",pack("d",2.5)); print $f2_5 . "\n"; # Will return something like 00000000000000000000000000000000000000000 +00000000010000000000010

    What I have done is to convert hex to binary then (possibly) shuffle the binary to a form suitable for this CPU and use pack/ unpack to assemble the float.

    { my($endian,$binary2double_fun); 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 $v = unpack("b64",pack("d",2.5)); if($v eq "00000000000000000000000000000000000000000000000000100000 +00000010") { # 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 "01000000000001000000000000000000000000000000000000000 +00000000000") { # *NIX hardware $endian = "ieee-big"; $binary2double_fun = sub { # a combination of pack and unpack will do it my $bits = shift; return(unpack("d",pack("b64",$bits))); }; } else { print STDERR "Not configured for this machine yet\n"; exit(20); } } sub hex2double { my($in,$out,$val) = @_; deduce_endian() if(!$endian); # The string we were given was a hex representation of # the bits of an IEEE 64' double in network order. # Convert to a string of 64 "0" and "1"s. We cannot # use pack here because then the order depends on the # hardware $out = hex2binary($in); # Then map this to our double representation return &{$binary2double_fun}($out) if($binary2double_fun); # No mapping function defined! return 0.0; } }

    I have my own hex2binary function for various reasons that are not worth dwelling on. It sounds like you want one that takes into account the byte order of the input.

Re: Converting Hex to Float (and vise versa)
by jmcnamara (Monsignor) on Feb 06, 2004 at 21:51 UTC

    Here is one way of doing it:
    #!/usr/bin/perl -wl my $hex = '0x428C0000'; $hex =~ s/0x//i; # Strip the hex 0x if present print unpack "f", reverse pack "H*", $hex ; print unpack "f", pack "H*", $hex ; __END__ Prints on an a little endian platform: 70 5.03150226600469e-041

    In this case the first of these looks like the right value.

    It is rare that you will encounter a platform where one of these doesn't work. If you do then I have some Perl code for packing and unpacking IEEE 754 floats and doubles independently of the architecture.


Re: Converting Hex to Float (and vise versa) (overly simple)
by tye (Cardinal) on Feb 06, 2004 at 22:05 UTC

    This should work on many platforms for the example data you gave:

    my $float= unpack "f", pack "L", 0x428C0000;

    (assuming the value you wanted was the nice-looking "70").

    but I'm not sure how to reconcile the terms "in ASCII" vs. "e.g. 0x428C0000". Perhaps you have a string like "0x428C0000":

    my $string= "0x428C0000"; my $float= unpack "f", pack "L", hex $string;
    or perhaps you have a string of bytes whose hex values are 42, 8C, 00, and 00:
    my $string= "\x42\x8C\x00\x00";
    in which case you could pick between these two, depending on whether the source and destination systems are the same or different endian:
    $float= unpack "f", $string; $float= unpack "f", scalar reverse $string;
    or you could use use two steps to auto-handle the receiving ends endianness (since your example data appears to show me the endianness of the source):
    my $string= "\x42\x8C\x00\x00"; my $float= unpack "f", pack "L", unpack "N", $string;

    - tye        

      $float= unpack "f", pack "L", 0x428C0000;
      print $float; # 70
      $float= unpack "L", pack "f", 70 ;
      print $float; # print 1116471296

      Please tye, could you just point me why?
Re: Converting Hex to Float (and vise versa)
by barrachois (Pilgrim) on Feb 09, 2004 at 21:51 UTC
    I would think the simplest is to let Perl do the work for you. Since 0x428C0000 is an allowed number format in Perl, just evaluate the string using eval() and let Perl translate it into a number for you.

    This trick will also work for the other allowed number formats, such as 0777 (leading 0 implies octal).

    For example,

    print " $_ is " . eval($_) . "\n" for qw( 0x428C000 0777 );
    I put together a CGI script to do this sort of conversion (and a few other IP network translations) which you might find instructive; see this link and the source code. Feel free to adopt as you see fit.

    - barrachois

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://327171]
Approved by Theo
Front-paged by tye
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2014-11-27 23:33 GMT
Find Nodes?
    Voting Booth?

    My preferred Perl binaries come from:

    Results (190 votes), past polls