in reply to Re: Mysteries of unpack("a", ...)
in thread Mysteries of unpack("a", ...)

Yah, that looks right. It didn't occur to me to unpack something I've already unpacked. I ended up grabbing substr's of an unpacked bitstring of my original data, in a rather ugly form.

And yes, I also had to correct my math. I'll change it back to (presumably more effecient) bit fiddling tomorrow. For now, here's what I've ended up with:

sub parse_E ($) { my $data = shift; my $databs = unpack ("B32", $data); my $sign = oct ("0b" . substr ($databs, 0, 1)) ? -1 : 1; my $characteristic = oct ("0b" . substr ($databs, 1, 7)) - 64; my $exponent = 16 ** $characteristic; my $fraction = oct ("0b" . substr ($databs, 8, 24)) / 0xfffff +f; my $num = $sign * $fraction * $exponent; return $num; }

Thanks much!

-- Pat

Replies are listed 'Best First'.
Re^3: Mysteries of unpack("a", ...)
by gone2015 (Deacon) on Jan 04, 2009 at 01:49 UTC

    I don't know why you're dividing the fraction part by 0xFF_FFFF, if you want to do it that way you'd surely want to divide by 0x100_0000.

    The following:

    my $f = unpack('N', pack('B32', "01000010001111000000000000000000")) +; printf "0x%08X = %12.9f\n", $f, conv_HFP($f) ; sub conv_HFP { my ($f) = @_ ; my $s = ($f & 0x8000_0000) ? -1 : +1 ; my $e = (((($f >> 24) & 0x7F) - 0x40) * 4) - 24 ; return $s * ($f & 0xFF_FFFF) * (2 ** $e) ; } ;
    will convert 4 byte 360/370 style radix 16 floats (where b31 is sign, b30..b24 is exponent biased by 0x40, and b23..0 is the fraction with binary point to the left of b23). Using the one example I can see the result is:
      0x423C0000 = 60.000000000
    
    From a quick poke around, it appears that floats are stored big-endian.

    I note that you say these are "IBM 390 E format 4 byte floats". The ESA/390 supports both the old 360/370 HFP (hex floating point) and new newer BFP (binary floating point) which conforms to IEEE 754-1985. Converting BFP can be done so:

    my $f = unpack('N', pack('B32', "01000010001111000000000000000000")) +; printf "0x%08X = %12.9f\n", $f, conv_BFP($f) ; sub conv_BFP { my ($f) = @_ ; my $s = ($f & 0x8000_0000) ? -1 : +1 ; my $e = ((($f >> 23) & 0xFF) - 0x7F) - 23 ; return $s * (($f & 0x7F_FFFF) | 0x80_0000) * (2 ** $e) ; } ;
    in the unlikely event your native floating point is not IEEE 754-1985 !! (Otherwise unpack('f', ...), with suitable care over the byte ordering !)