2016-12-15 87 views
1

我有一些浮点数的文件,我有问题找出他们的确切编码。我已经尝试了几种方法转换为标准双精度值,但没有太多运气。我知道这些值应该转换为什么,但需要一种方法直接从文件中提取。惠普2114/15/16浮点转换

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

除了一些旧的,不可靠的文档(无法使用声明的格式进行转换)以及来自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 

的其他文件不清晰,但似乎说这是所有二进制补码。 我试过了我发现的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 
    } 

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

+0

从[浮点格式](HTTP:/ /www.oneonta.edu/faculty/zhangs/csci201/general%20Floating%20Point%20Format.htm):*浮点数以2的补码尾数开始,然后结束w第七位指数,后面是指数的符号,当数字是负数时,两者都不会互补。浮点数可以占用两个或三个16位字,具体取决于它们是单精度还是双精度。* –

+0

@Andy前导七个字节看起来是二进制补码尾数。看一下-20.8,经过适当的缩放后,转换为'ACCCC ...'作为二进制补码。我遇到的问题是弄清楚最后一个字节'0A'如何映射到指数。你有不同的binade的额外的示例值?这大概会澄清指数编码。 – njuffa

+0

我的意思是“领先*五*字节”代表二进制补码尾数。所以这个值是('0xACCCCCCCCC'/2^39)* 2^expo。 – njuffa

回答

2

Hewlett-Packard Journal, October 1978的一篇文章,皮克。 14给出了关于这里使用的浮点格式的下列信息:

扩展精度数有39位有符号尾数和相同 七位符号指数。所有尾数都归一化,这意味着它们在范围[½,1)和[-1,-½)内。

在问题的信息一起考虑,这意味着尾数一个签名,二进制补码,40位整数由前5个字节表示,并且必须由2 被划分以获得它的数值浮点值。

二进制指数存储在最后一个字节中。基于在期刊文章中将40位尾数字段描述为39位尾数,而指数被描述为具有七位,连同来自问题的指数符号位的信息,似乎指数是一个带符号的二进制补码8位整数,它已被左旋1位,以使其符号位在指数字节的位[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 
+0

试过以上。使用c#,绝对值函数是Math.Abs​​(value)。这会引发异常。上一行是mant =(Int64)(Math.Pow(mant,(1 << 39)) - (1 << 39));这导致0x8000 000000000000表示-0。我猜Pow不喜欢那样。在c#中没有exp2或llabs。 C99和C#之间的处理方式都不一样,或者我的大脑被炸,因为我没有看到真正的不同。 – Andy

+0

@安迪对不起,我根本不知道C#。请注意,'^'是C(和C++)*中的XOR运算符,而不是* exponentation! 'llabs'是64位整数的绝对值函数。注意'1LL << 39'的使用,'LL'将'1'标记为64位值。我认为'exp2(x)'可以是C#中的'Math.Pow(2.0,x)'。 – njuffa

+0

@Andy [Microsoft C#documentation](https://msdn.microsoft.com/en-us/library/6a71f45d.aspx)告诉我C#也使用'^'作为按位XOR运算符,就像C和C++一样。所以我认为你可以复制更多或逐字。在我的代码中,我将调用改为'exp2'来调用'pow',这可能使得直接转换为C#变得更容易。 – njuffa

1

实施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 
1

njuffa's代码转换成德尔福:

  • 尾数是左移16位形成一个64位有符号整数。
  • 指数被提取并右移1位以形成一个带符号的8位整数。
  • 所得双由尾数乘以缩放与IntPower(2,-63+Exponent)

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