chrism01 has asked for the wisdom of the Perl Monks concerning the following question:

OK, here's the deal:
Src box:
Prod server running FreeBSD 4.9-STABLE, perl 5.005_03
Tgt box:
Linux (RH FC3) kernel 2.6.9, perl v5.8.5

Src is sending concatenated long doubles:
$s.= pack('D',$count{$ip}{$x}{in}); $s.= pack('D',$count{$ip}{$x}{out}); $s.= pack('D',int $count{$ip}{$x}{in}); $s.= pack('D',int $count{$ip}{$x}{out}); $s.= pack('D',int $count{$ip}{$x}{inpackets}); $s.= pack('D',int $count{$ip}{$x}{outpackets});
This cannot be changed as many boxes depend already on output.

Tgt unpacks:
unpack('d',substr($msg,$ptr,8)); unpack('d',substr($msg,$ptr+8,8)); unpack('d',substr($msg,$ptr+16,8)); unpack('d',substr($msg,$ptr+24,8));
Obviously sometimes the incoming num is too big and corruption occurs. Perldoc on tgt says:

D A long double-precision float in the native format. (Long doubles are available only if your system supports long double values _and_ if Perl has been compiled to support those. Causes a fatal error otherwise.)

Tgt box has not been recompiled.
Qn: will RH FC3 support long doubles if we try to re-compile?
We would prefer not to re-compile if possible, as tgt is prod box also.
Is it possible to unpack using a different method eg unpack each block of 8 bytes as 2 sets of 4, then re-combine?
Thx for any help
Chris

Replies are listed 'Best First'.
Re: Value packed as 'D', how to unpack not using 'D'?
by Roy Johnson (Monsignor) on Apr 28, 2005 at 19:41 UTC
    You will have to figure out the size and format of a native long double on the packing system. You can unpack the bytes using 'C*', then it would be up to you to interpret those bytes the same way the packing system did.

    Caution: Contents may have been coded under pressure.
Re: Value packed as 'D', how to unpack not using 'D'?
by Anonymous Monk on Apr 28, 2005 at 01:49 UTC
    Why do you pack as 'D' but unpack as 'd'? They are different pack templates.
      It's an inherited situation...
      As I said, the Tgt box will not accept 'D' as a valid unpack format without a full re-compile of Perl, assuming the underlying system will accept it, as per the quoted perldoc
Re: Value packed as 'D', how to unpack not using 'D'?
by BrowserUk (Patriarch) on Apr 28, 2005 at 21:35 UTC

    You will need to determine what format the long doubles are in, and the byte-ordering of the platform that is writing them.

    There are many 128-bit real formats around.

    • The FORTRAN REAL*16, (sometimes also referred to as IEEE quadruple format) which uses a 16-bit mantissa and 112-bit characteristic).
    • However, some C-compilers (notably MSC because of the 80-bit format used by Intel FP processors.), use an 80-bit real--15-bit mantissa and 63-bits characteristic--(Also known as FORTRAN REAL*10 and IEEE Extended Double), padded out to 128-bits with always-0 bits, when a long double is called for.
    • When I was looking around at this before, I also came across a description of a 128-bit real format that consisted of 2 64-bit reals concatenated. The two were summed to produce the actual value. The effect was to give the same approx 2E+-308 range, but an effective precision of (from memory) 106 bits.

      I cannot relocate that description now.

    There are others besides these, including some mainframes use a kind of binary coded decimal format.

    To compound the problem, there is no defined "network format" for reals, so when they are written to storage in native format, they tend to be byte-order dependant. Even if you write a 64-bit real using pack 'd' format on a little-endian platform, you will not be able to directly unpack it using 'd' on a big-endian platform.

    Once you know the format and endianess of the data, the easiest way to unpack it would be to transform it into a ascii-ized bitstring, break it up into its component parts, and then reconstitute it from there.

    By way of example of what you need to do, the following code constructs some random 64-bit reals and then decodes them without using pack 'd'.

    The output compares pack 'd' with the results from my real8ToDouble() routine.

    #! perl -slw use strict; sub real8ToDouble { my( $real8 ) = shift; my $bits = reverse unpack 'b64', $real8; # IEEE 754 64-bit real (double) format # seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff f +fffffff #1-sign 11- mantissa 52-bits (+1 implicit) binary fraction my( $sign, $exp, $frac ) = $bits =~ m[(.)(.{11})(.{52})]; my $expAsInt = unpack( 'v', pack 'b16', scalar reverse $exp ) - 10 +23; my $f = 1; my $n = 1; for my $bp ( split '', $frac ) { $n /= 2; $f += $n if $bp; } return ( $f * 2**$expAsInt ) * ( $sign ? -1 : 1 ); } for ( 1 .. 20 ) { my( $s, $c, $m ) = ( rand > 0.5 ? '+' : '-', rand, int( rand 600 ) -300 ); my $double = "$s${c}E$m"; printf "%g : %g\n", $double, real8ToDouble( pack 'd', $double ); } __END__ P:\test>452186.pl -3.27454e+126 : -3.27454e+126 -6.33606e-135 : -6.33606e-135 -9.78943e-089 : -9.78943e-089 3.75458e-262 : 3.75458e-262 9.11469e-049 : 9.11469e-049 6.66809e-052 : 6.66809e-052 9.58191e+086 : 9.58191e+086 -7.59277e+284 : -7.59277e+284 3.93188e-021 : 3.93188e-021 -4.28009e-048 : -4.28009e-048 -5.32776e-173 : -5.32776e-173 5.55817e-147 : 5.55817e-147 -4.44031e+060 : -4.44031e+060 2.40295e+126 : 2.40295e+126 8.55194e+132 : 8.55194e+132 5.61005e+184 : 5.61005e+184 -9.57489e-107 : -9.57489e-107 8.71338e+130 : 8.71338e+130 5.66986e+227 : 5.66986e+227 3.41461e-116 : 3.41461e-116

    Obviously you will lose some precision going from 128-bits to 64-bits, and extreme values will fall outside of the range a 64-bit real can handle.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco.
    Rule 1 has a caveat! -- Who broke the cabal?