HP 2114/15/16 floating point conversion
<p>I have some files with floating point values in them that I'm having problems figuring out their exact encoding.  I've tried several ways to convert to a standard double value, but haven't had much luck.  I know what the values are supposed to convert to, but need a method to extract directly from the files.</p>

<p>These are HP-1000 (HP21xx) series floating point values.  Similar to 48 bit Pascal, but not the same.</p>

<p>Other than some old, unreliable documentation (couldn't get a conversion using what it claimed was the format), and a list of formats from quadibloc.com I have found nothing else.  Format from quadibloc gives the following:</p>

<pre><code>(Note, big-endian order)

MSb: mantissa sign (says it's 2s complement)

39 bits: mantissa

7 bits: exponent

1 bit: exponent sign
</code></pre>

<p>The other documentation isn't clear, but seems to say it's all 2s complement.
I've tried the Pascal conversion code I found, but it doesn't even come close. (after moving the exponent sign to high bit of the byte).</p>

<p>Examples, converted using a PPC that emulates an HP-1000:</p>

<pre><code>A7EB851EB90A    -22.02

A870A3D70A0A    -21.89

A8AE147AE20A    -21.83

A8E147AE140A    -21.78

A9666666670A    -21.65

AC70A3D70A0A    -20.89

ACC28F5C290A    -20.81

ACCCCCCCCC0A    -20.8

AE70A3D70B0A    -20.39

AEB851EB850A    -20.32
</code></pre>

<p>This was from only one file.  I have at least 100 of these files to extract from.</p>

<p>Any ideas?  Sure would be appreciated.</p>

<p>Edit: I guess I should have said what language I am working in.  In this case, it's C#.  As for additional data, I have a ton of it.  More examples here, these include negative exponents, at least when converted to decimal.</p>

<pre><code>400000000002    1
43851EB85204    2.11
451EB851EB04    2.16
4C7AE147AE04    2.39
4EC4EC4EC5F3    4.8076923077E-03
519999999A04    2.55
5838B6BE9BF3    5.3846153846E-03
5B851EB85202    1.43
5BD70A3D7108    11.48
5C7AE147AE08    11.56
5E51EB851E08    11.79
5FD70A3D7108    11.98
62E147AE1508    12.36
64A3D70A3E08    12.58
666666666702    1.6
67AE147AE202    1.62
6B204B9E54F3    6.5384615385E-03
733333333302    1.8
762762762AF3    7.2115384616E-03
794DFB4619F3    7.4038461538E-03
7C74941627F3    7.5961538465E-03
7E07E07E14F3    7.6923076925E-03
</code></pre>

<p>Should have included positive numbers before.</p>

<p>Tried the suggestions, but in C#.  Wildly different results.  I even split out the bits of the calculations, but I get very high numbers compared to the numbers given in the suggestions.  Here's the code with the bits expanded.</p>

<pre><code>    Int64[] test_data = new Int64[] {
        0xA7EB851EB90A, 0xA870A3D70A0A, 0xA8AE147AE20A, 0xA8E147AE140A,
        0xA9666666670A, 0xAC70A3D70A0A, 0xACC28F5C290A, 0xACCCCCCCCC0A,
        0xAE70A3D70B0A, 0xAEB851EB850A, 0x400000000002, 0x43851EB85204,
        0x451EB851EB04, 0x4C7AE147AE04, 0x4EC4EC4EC5F3, 0x519999999A04,
        0x5838B6BE9BF3, 0x5B851EB85202, 0x5BD70A3D7108, 0x5C7AE147AE08,
        0x5E51EB851E08, 0x5FD70A3D7108, 0x62E147AE1508, 0x64A3D70A3E08,
        0x666666666702, 0x67AE147AE202, 0x6B204B9E54F3, 0x733333333302,
        0x762762762AF3, 0x794DFB4619F3, 0x7C74941627F3, 0x7E07E07E14F3
    };

    private void Checkit()
    {
        Byte[] td = new Byte[6];                        // 6 byte array for reversed data
        Byte[] ld = new Byte[8];                        // 8 byte conversion array

        for (Int32 i = 0; i &lt; test_data.Length; i++)    // Loop through data
        {
            ld = BitConverter.GetBytes(test_data[i]);   // Get value as byte array
            for (Int32 j = 0; j &lt; 6; j++)               // Copy the bytes in reverse
                td[5 - j] = ld[j];
            // Go test them
            Console.WriteLine("{0:X6}  --&gt; {1:E}", test_data[i], Real48ToDouble(ref td));
        }
    }

    Double Real48ToDouble(ref Byte[] realValue)
    {
        // Values are using input value of A7EB851EB90A and 7E07E07E14F3
        if (realValue[0] == 0)
            return 0.0;                                     // Null exponent = 0

        Byte[] b = new Byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };  // 64 bit byte array
        for (Int32 i = 0; i &lt; 5; i++)                       // Copy over the 48 bit info
            b[4 - i] = realValue[i];

        Int64 mant = BitConverter.ToInt64(b, 0);            // 0x000000A7EB851EB9 - Get mantissa with sign
                                                            // 0x0000007E07E07E14
        Int32 expo = realValue[5];                          // 0x0000000A - Grab the exponent
                                                            // 0x000000F3
        Int32 mant_sign = (Int32)(mant &gt;&gt; 39);              // 0x00000001
                                                            // 0x00000000

        // sign extend mantissa from 40 to 64 bits, then take absolute value
        mant = (Int64)((mant ^ (1L &lt;&lt; 39)) - (1L &lt;&lt; 39));   // 0xFFFFFFA7EB851EB9 - First calc
                                                            // 0x0000007E07E07E14
        mant = Math.Abs(mant);                              // 0x00000058147AE147 - Make absolute
                                                            // 0x0000007E07E07E14

        // convert mantissa to floating-point
        Double fmant = mant * Math.Pow(2, -39.0);           // 0.68812499999876309 - Second calc
                                                            // 0.98461538463743636
        // rotate exponent field right by 1 bit
        expo = (expo &gt;&gt; 1) | ((expo &amp; 1) &lt;&lt; 7);             // 0x00000005
                                                            // 0x000000F9
        // sign extend exponent from 8 to 32 bits
        expo = ((expo ^ (1 &lt;&lt; 7)) - (1 &lt;&lt; 7));              // 0x00000005
                                                            // 0xFFFFFFF9

        // compute scale factor from exponent field
        Double scale = Math.Pow(2, expo);                   // 32.0 - Scale
                                                            // 0.0078125

        // scale mantissa, and apply sign bit for final result
        Double num = fmant * scale;                         // 22.019999999960419 - Make the final abs number
                                                            // 0.0076923076924799716

        return (mant_sign != 0) ? (-num) : num;             // -22.019999999960419 - Return with sign
                                                            // 0.0076923076924799716
    }
</code></pre>

<p>Changed code above
The code above is now working correctly.</p>