HP 2114/15/16浮点转换

时间:2016-12-15 22:30:52

标签: floating-point converter pascal

我有一些浮点值的文件,我在确定它们的确切编码时遇到了问题。我已经尝试了几种转换为标准双倍价值的方法,但是没有多少运气。我知道值应该转换为什么,但需要一种方法直接从文件中提取。

这些是HP-1​​000(HP21xx)系列浮点值。类似于48位Pascal,但不一样。

除了一些旧的,不可靠的文档(无法使用它声称的格式进行转换),以及来自quadibloc.com的格式列表,我没有找到任何其他内容。 quadibloc格式提供以下内容:

(Note, big-endian order)

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

39 bits: mantissa

7 bits: exponent

1 bit: exponent sign

其他文件并不清楚,但似乎说这是所有2的补充。 我已经尝试过我发现的Pascal转换代码,但它甚至没有接近。 (将指数符号移动到字节的高位后)。

使用模拟HP-1000的PPC转换的示例:

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

这只来自一个文件。我要从中提取至少100个这样的文件。

有什么想法吗?当然会受到赞赏。

编辑:我想我应该说我正在使用的语言。在这种情况下,它是C#。至于其他数据,我有很多。这里有更多的例子,其中包括负指数,至少在转换为十进制时。

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

之前应该包含正数。

尝试了这些建议,但是在C#中。结果差异很大。我甚至将计算的部分分开,但与建议中给出的数字相比,我的数字非常高。这是扩展位的代码。

    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 < test_data.Length; i++)    // Loop through data
        {
            ld = BitConverter.GetBytes(test_data[i]);   // Get value as byte array
            for (Int32 j = 0; j < 6; j++)               // Copy the bytes in reverse
                td[5 - j] = ld[j];
            // Go test them
            Console.WriteLine("{0:X6}  --> {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 < 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 >> 39);              // 0x00000001
                                                            // 0x00000000

        // sign extend mantissa from 40 to 64 bits, then take absolute value
        mant = (Int64)((mant ^ (1L << 39)) - (1L << 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 >> 1) | ((expo & 1) << 7);             // 0x00000005
                                                            // 0x000000F9
        // sign extend exponent from 8 to 32 bits
        expo = ((expo ^ (1 << 7)) - (1 << 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
    }

更改了上面的代码 上面的代码现在正常工作。

3 个答案:

答案 0 :(得分:2)

Hewlett-Packard Journal, October 1978中的一篇文章,pg。图14给出了有关此处使用的浮点格式的以下信息:

  

扩展精度数字具有39位带符号的尾数并且相同   七位有符号指数。所有尾数都是标准化的,这意味着   它们的范围是[½,1)和[-1,-½]。

与问题中的信息一起考虑这意味着尾数是由前五个字节表示的带符号的二进制补码,40位整数,并且必须除以2 39 才能获得它的数值浮点值。

二进制指数存储在最后一个字节中。基于期刊文章中40位尾数字段作为39位尾数的描述,虽然描述了具有7位的指数,以及来自问题的指数符号位的信息,但似乎指数是一个带符号的二进制补码8位整数,它已向左旋转一位,使其符号位在指数字节的位[0]中结束。要处理它,我们需要向右旋转一点。

基于以上信息,我编写了以下C99程序,该程序成功解码了问题中的测试向量:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

int64_t test_data [] =
{
    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
};
#define NBR_TEST_VECTORS (sizeof(test_data) / sizeof(int64_t))

int main (void)
{
    for (int i = 0; i < NBR_TEST_VECTORS; i++) {

        // extract mantissa and exponent bits
        int64_t mant = test_data[i] >> 8;
        int expo = test_data[i] & 0xFF;
        int mant_sign = mant >> 39;

        // sign extend mantissa from 40 to 64 bits, then take absolute value
        mant = ((mant ^ (1LL << 39)) - (1LL << 39));
        mant = llabs (mant);

        // convert mantissa to floating-point
        double fmant = mant * pow (2.0, -39.0);

        // rotate exponent field right by 1 bit
        expo = (expo >> 1) | ((expo & 1) << 7);

        // sign extend exponent from 8 to 32 bits
        expo = ((expo ^ (1 << 7)) - (1 << 7));

        // compute scale factor from exponent field
        double scale = pow (2.0, expo);

        // scale mantissa, and apply sign bit for final result
        double num = fmant * scale;
        num = mant_sign ? -num : num;

        printf ("%012llx  --> % 20.11e\n", test_data[i], num);
    }
    return EXIT_SUCCESS;
}

上述程序的输出应如下所示:

a7eb851eb90a  -->  -2.20200000000e+001
a870a3d70a0a  -->  -2.18900000000e+001
a8ae147ae20a  -->  -2.18300000000e+001
a8e147ae140a  -->  -2.17800000000e+001
a9666666670a  -->  -2.16500000000e+001
ac70a3d70a0a  -->  -2.08900000000e+001
acc28f5c290a  -->  -2.08100000000e+001
accccccccc0a  -->  -2.08000000000e+001
ae70a3d70b0a  -->  -2.03900000000e+001
aeb851eb850a  -->  -2.03200000000e+001
400000000002  -->   1.00000000000e+000
43851eb85204  -->   2.11000000000e+000
451eb851eb04  -->   2.16000000000e+000
4c7ae147ae04  -->   2.39000000000e+000
4ec4ec4ec5f3  -->   4.80769230769e-003
519999999a04  -->   2.55000000000e+000
5838b6be9bf3  -->   5.38461538456e-003
5b851eb85202  -->   1.43000000000e+000
5bd70a3d7108  -->   1.14800000000e+001
5c7ae147ae08  -->   1.15600000000e+001
5e51eb851e08  -->   1.17900000000e+001
5fd70a3d7108  -->   1.19800000000e+001
62e147ae1508  -->   1.23600000000e+001
64a3d70a3e08  -->   1.25800000000e+001
666666666702  -->   1.60000000000e+000
67ae147ae202  -->   1.62000000000e+000
6b204b9e54f3  -->   6.53846153847e-003
733333333302  -->   1.80000000000e+000
762762762af3  -->   7.21153846158e-003
794dfb4619f3  -->   7.40384615382e-003
7c74941627f3  -->   7.59615384651e-003
7e07e07e14f3  -->   7.69230769248e-003

答案 1 :(得分:1)

在Delphi中实现 njuffa 方法

type
  THexabyte = array[0..5] of Byte;

function ConvertHPToDouble(const Data: THexabyte): Double;
var
  iexp: Integer;
  pbi: PByteArray;
  i64: Int64;
begin
  iexp := Data[5] shr 1;  //unsigned shift
  if (Data[5] and 1) <> 0 then    //use exp sign
     iexp := - iexp;
  pbi := @i64;
  pbi[0] := 0;
  pbi[1] := 0;
  pbi[2] := 0;
  pbi[3] := Data[4];    //shuffle bytes into intel order
  pbi[4] := Data[3];
  pbi[5] := Data[2];
  pbi[6] := Data[1];
  pbi[7] := Data[0];
  i64 := i64 div (1 shl 23);  //arithmetic shift saves sign
  Result := i64 / (Int64(1) shl (40 - iexp));
end;


var
  Data: THexabyte;
  i: Integer;
  s: string;
begin
  s := 'A870A3D70A0A';
  for i := 0 to 5 do
    Data[i] := StrToInt('$' + Copy(s, i * 2 + 1, 2));
  Memo1.Lines.Add(Format('%14.7f', [ConvertHPToDouble(Data)]));
  // gives    -21.8900000

答案 2 :(得分:1)

njuffa's代码转换为Delphi:

  
      
  • 尾数左移16位,形成64位有符号整数。
  •   
  • 提取指数并向右旋转1位以形成带符号的8位整数。
  •   
  • 通过将尾数乘以IntPower(2,-63+Exponent)
  • 来缩放得到的double   
program HP_Float_To_Double;    
{$APPTYPE CONSOLE}    
uses
  System.SysUtils,Math;    
var
  test_data : TArray<Int64> = [
      $A7EB851EB90A, $A870A3D70A0A, $A8AE147AE20A, $A8E147AE140A,
      $A9666666670A, $AC70A3D70A0A, $ACC28F5C290A, $ACCCCCCCCC0A,
      $AE70A3D70B0A, $AEB851EB850A, $400000000002, $43851EB85204,
      $451EB851EB04, $4C7AE147AE04, $4EC4EC4EC5F3, $519999999A04,
      $5838B6BE9BF3, $5B851EB85202, $5BD70A3D7108, $5C7AE147AE08,
      $5E51EB851E08, $5FD70A3D7108, $62E147AE1508, $64A3D70A3E08,
      $666666666702, $67AE147AE202, $6B204B9E54F3, $733333333302,
      $762762762AF3, $794DFB4619F3, $7C74941627F3, $7E07E07E14F3];

function Exponent( b: Byte): ShortInt;
begin
  Result := (b shr 1) or ((b and 1) shl 7);
end;

function HPFloatToDouble( HP: Int64): Double;
var
  Exp: ShortInt;
begin
  HP := HP shl 16; // Shift left 16 bits
  Exp := Exponent(PByte(@HP)[2]); // Rotate exponent right 1 position into signed 8 bit
  HP := (HP and $FFFFFFFFFF000000); // Clear exponent part from mantissa
  Result := HP*IntPower(2,-63+Exp); // Scale result
end;

var
  I64 : Int64;
begin
  for I64 in test_data do
    WriteLn(Format('%x -> %20.11e',[I64, HPFloatToDouble(I64)]));
  ReadLn;
end.

输出如下:

A7EB851EB90A ->   -2,2020000000E+001
A870A3D70A0A ->   -2,1890000000E+001
A8AE147AE20A ->   -2,1830000000E+001
A8E147AE140A ->   -2,1780000000E+001
A9666666670A ->   -2,1650000000E+001
AC70A3D70A0A ->   -2,0890000000E+001
ACC28F5C290A ->   -2,0810000000E+001
ACCCCCCCCC0A ->   -2,0800000000E+001
AE70A3D70B0A ->   -2,0390000000E+001
AEB851EB850A ->   -2,0320000000E+001
400000000002 ->    1,0000000000E+000
43851EB85204 ->    2,1100000000E+000
451EB851EB04 ->    2,1600000000E+000
4C7AE147AE04 ->    2,3900000000E+000
4EC4EC4EC5F3 ->    4,8076923077E-003
519999999A04 ->    2,5500000000E+000
5838B6BE9BF3 ->    5,3846153846E-003
5B851EB85202 ->    1,4300000000E+000
5BD70A3D7108 ->    1,1480000000E+001
5C7AE147AE08 ->    1,1560000000E+001
5E51EB851E08 ->    1,1790000000E+001
5FD70A3D7108 ->    1,1980000000E+001
62E147AE1508 ->    1,2360000000E+001
64A3D70A3E08 ->    1,2580000000E+001
666666666702 ->    1,6000000000E+000
67AE147AE202 ->    1,6200000000E+000
6B204B9E54F3 ->    6,5384615385E-003
733333333302 ->    1,8000000000E+000
762762762AF3 ->    7,2115384616E-003
794DFB4619F3 ->    7,4038461538E-003
7C74941627F3 ->    7,5961538465E-003
7E07E07E14F3 ->    7,6923076925E-003