如何将两个字节转换为浮点数

时间:2019-03-09 21:31:07

标签: vb.net floating-point binary

我有一些需要挖掘数据的旧文件。这些文件是由Lotus123 Release 4 for DOS创建的。我试图通过解析字节而不是使用Lotus打开文件来更快地读取文件。

Dim fileBytes() As Byte = My.Computer.FileSystem.ReadAllBytes(fiPath)

'I loop through all the data getting first/second bytes for each value
do ...
    Dim FirstByte As Int16 = Convert.ToInt16(fileBytes(Index))
    Dim SecondByte As Int16 = Convert.ToInt16(fileBytes(Index + 1))
loop ...

我可以得到这样的整数值:

Dim value As Int16 = BitConverter.ToInt16(fileBytes, Index + 8) / 2

但是浮点数更加复杂。只有较小的数字存储有两个字节。较大的值占用10个字节,但这是另一个问题。在这里,我们只有两个字节的较小值。以下是一些示例值。我将字节值输入Excel,然后使用= DEC2BIN()转换为二进制,并根据需要在左侧添加零以获得8位。

First   Second
Byte    Byte        Value   First   Byte    2nd     Byte

7       241     =   -1.2    0000    0111    1111    0001
254     255     =   -1      1111    1110    1111    1111
9       156     =   -0.8    0000    1001    1001    1100
9       181     =   -0.6    0000    1001    1011    0101
9       206     =   -0.4    0000    1001    1100    1110
9       231     =   -0.2    0000    1001    1110    0111
13      0       =   0       0000    1101    0000    0000
137     12      =   0.1     1000    1001    0000    1100
9       25      =   0.2     0000    1001    0001    1001
137     37      =   0.3     1000    1001    0010    0101
9       50      =   0.4     0000    1001    0011    0010
15      2       =   0.5     0000    1111    0000    0010
9       75      =   0.6     0000    1001    0100    1011
137     87      =   0.7     1000    1001    0101    0111
9       100     =   0.8     0000    1001    0110    0100
137     112     =   0.9     1000    1001    0111    0000
2       0       =   1       0000    0010    0000    0000
199     13      =   1.1     1100    0111    0000    1101
7       15      =   1.2     0000    0111    0000    1111
71      16      =   1.3     0100    0111    0001    0000
135     17      =   1.4     1000    0111    0001    0001
15      6       =   1.5     0000    1111    0000    0110
7       20      =   1.6     0000    0111    0001    0100
71      21      =   1.7     0100    0111    0001    0101
135     22      =   1.8     1000    0111    0001    0110
199     23      =   1.9     1100    0111    0001    0111
4       0       =   2       0000    0100    0000    0000

我希望有一种简单的转换方法。也许会更复杂。

  

我看过BCD:“ BCD在许多早期十进制计算机中使用,并且在诸如IBM System / 360系列之类的机器指令集中实现”和Intel BCD opcode

我不知道这是BCD还是什么。如何将两位转换为浮点数?

2 个答案:

答案 0 :(得分:3)

我使用了安德鲁·莫顿(Andrew Morton)在评论中指出的website中的信息。基本上,存储的16位数量由15位二进制补码整数(当lsb为0时)或12位二进制补码整数加上指示要应用于该整数的比例因子的处理代码(当lsb时)组成是1)。我对vb.net不熟悉,因此在这里提供ISO-C代码。下面的程序成功解码了问题中提供的所有数据。

注意:我在下面的代码中转换为8字节的double,而问题是,原来的转换可能是10字节的long double格式(扩展了80位-8087数学协处理器的精度格式)。尝试更多的测试数据以完全覆盖八个定标代码似乎是一个好主意:大整数,如1,000,000和1,000,000,000;小数部分,例如0.0003、0.000005和0.00000007;和二进制分数,如0.125(1/8)和0.046875(3/64)。

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

typedef struct {
    uint8_t byte1;
    uint8_t byte2;
} num;

num data[] = 
{
    {  7, 241}, {254, 255}, {  9, 156}, {  9, 181}, {  9, 206}, {  9, 231},
    { 13,   0}, {137,  12}, {  9,  25}, {137,  37}, {  9,  50}, { 15,   2},
    {  9,  75}, {137,  87}, {  9, 100}, {137, 112}, {  2,   0}, {199,  13},
    {  7,  15}, { 71,  16}, {135,  17}, { 15,   6}, {  7,  20}, { 71,  21},
    {135,  22}, {199,  23}, {  4,   0}
};

int data_count = sizeof (data) / sizeof (data[0]);

/* define operators that may look more familiar to vb.net programmers */
#define XOR  ^
#define MOD  %

int main (void)
{
    int i;
    uint8_t b1, b2;
    uint16_t h, code;
    int32_t n;
    double r;
    for (i = 0; i < data_count; i++) {
        b1 = data[i].byte1;
        b2 = data[i].byte2;
        /* data word */
        h = ((uint16_t)b2 * 256) + b1;
        /* h<0>=1 indicates stored integer needs to be scaled */
        if ((h MOD 2) == 1) {
            /* extract scaling code in h<3:1> */
            code = (h / 2) MOD 8; 
            /* scaled 12-bit integer in h<15:4>. Extract, sign-extend to 32 bits */
            n = (int32_t)((((uint32_t)h / 16) XOR 2048) - 2048);
            /* convert integer to floating-point */
            r = (double)n;
            /* scale based on scaling code */
            switch (code) {
            case 0x0:  r = r * 5000;  break;
            case 0x1:  r = r * 500;   break;
            case 0x2:  r = r / 20;    break;
            case 0x3:  r = r / 200;   break;
            case 0x4:  r = r / 2000;  break;
            case 0x5:  r = r / 20000; break;
            case 0x6:  r = r / 16;    break;
            case 0x7:  r = r / 64;    break;
            };
        } else {
            /* unscaled 15-bit integer in h<15:1>. Extract, sign extend to 32 bits */
            n = (int32_t)((((uint32_t)h / 2) XOR 16384) - 16384);
            /* convert integer to floating-point */
            r = (double)n;
        }
        printf ("[%3d,%3d]  n=%08x  r=% 12.8f\n", b1, b2, n, r);
    }
    return EXIT_SUCCESS;
}

该程序的输出如下:

[  7,241]  n=ffffff10  r= -1.20000000
[254,255]  n=ffffffff  r= -1.00000000
[  9,156]  n=fffff9c0  r= -0.80000000
[  9,181]  n=fffffb50  r= -0.60000000
[  9,206]  n=fffffce0  r= -0.40000000
[  9,231]  n=fffffe70  r= -0.20000000
[ 13,  0]  n=00000000  r=  0.00000000
[137, 12]  n=000000c8  r=  0.10000000
[  9, 25]  n=00000190  r=  0.20000000
[137, 37]  n=00000258  r=  0.30000000
[  9, 50]  n=00000320  r=  0.40000000
[ 15,  2]  n=00000020  r=  0.50000000
[  9, 75]  n=000004b0  r=  0.60000000
[137, 87]  n=00000578  r=  0.70000000
[  9,100]  n=00000640  r=  0.80000000
[137,112]  n=00000708  r=  0.90000000
[  2,  0]  n=00000001  r=  1.00000000
[199, 13]  n=000000dc  r=  1.10000000
[  7, 15]  n=000000f0  r=  1.20000000
[ 71, 16]  n=00000104  r=  1.30000000
[135, 17]  n=00000118  r=  1.40000000
[ 15,  6]  n=00000060  r=  1.50000000
[  7, 20]  n=00000140  r=  1.60000000
[ 71, 21]  n=00000154  r=  1.70000000
[135, 22]  n=00000168  r=  1.80000000
[199, 23]  n=0000017c  r=  1.90000000
[  4,  0]  n=00000002  r=  2.00000000

答案 1 :(得分:2)

只需VB.Net代码posted by njuffaC转换。
原始的structure已替换为Byte数组,并且数值数据类型适用于.Net类型。就这样。

Dim data As Byte(,) = New Byte(,) {
    {7, 241}, {254, 255}, {9, 156}, {9, 181}, {9, 206}, {9, 231}, {13, 0}, {137, 12}, {9, 25}, 
    {137, 37}, {9, 50}, {15, 2}, {9, 75}, {137, 87}, {9, 100}, {137, 112}, {2, 0}, {199, 13}, 
    {7, 15}, {71, 16}, {135, 17}, {15, 6}, {7, 20}, {71, 21}, {135, 22}, {199, 23}, {4, 0}
}

Dim byte1, byte2 As Byte
Dim word, code As UShort
Dim nValue As Integer
Dim result As Double

For i As Integer = 0 To (data.Length \ 2 - 1)
    byte1 = data(i, 0)
    byte2 = data(i, 1)
    word = (byte2 * 256US) + byte1
    If (word Mod 2) = 1 Then
        code = (word \ 2US) Mod 8US
        nValue = ((word \ 16) Xor 2048) - 2048
        Select Case code
            Case 0 : result = nValue * 5000
            Case 1 : result = nValue * 500
            Case 2 : result = nValue / 20
            Case 3 : result = nValue / 200
            Case 4 : result = nValue / 2000
            Case 5 : result = nValue / 20000
            Case 6 : result = nValue / 16
            Case 7 : result = nValue / 64
        End Select
    Else
        'unscaled 15-bit integer in h<15:1>. Extract, sign extend to 32 bits
        nValue = ((word \ 2) Xor 16384) - 16384
        result = nValue
    End If
    Console.WriteLine($"[{byte1,3:D}, {byte2,3:D}]  number = {nValue:X8} result ={result,12:F8}")
Next