65.9K
CodeProject 正在变化。 阅读更多。
Home

解释 Intel 80 位长双字节数组

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.57/5 (20投票s)

2004 年 4 月 6 日

4分钟阅读

viewsIcon

54599

downloadIcon

541

一个简单的 BitConverter 类,能够读取和写入 Intel 80 位长双精度数。

摘要

我最近在与其他系统集成时,发现需要从二进制流中读取 Intel 80 位长双精度数。出于跨平台兼容性的考虑,微软决定不在框架中包含长双精度(也称为 extended)类型,因此我不得不自己解析这些字节。

这不是长双精度类型的实现,而是一个 BitConverter 类,它可以在长双精度字节数组和常规 double 之间进行转换。当然,在此过程中会损失一定的范围和精度;但对我的目的来说,这是可以接受的。

引言

长双精度是一种浮点数,比常规 double 大 16 位。这些额外的位用于增加数字的范围和精度,通常用于数学和科学计算。

本文介绍了如何读取长双精度字节数组并从中创建常规 double 值。反向操作只是一个颠倒过程的问题,这里不再赘述。

问题

第一步是确定不同类型的长双精度及其等效的常规 double。定义每种状态的规则可以在 IEEE 标准 754 规范中找到(请参见参考文献)。

长双精度 双精度等效值
不支持 不支持
正常 零、非规格化数、规格化数或溢出(取决于e
非规格化数 Zero
伪非规格化数 Zero
带符号零 Zero
正无穷 正无穷
负无穷 负无穷
静默 NaN NaN
信令 NaN NaN

长双精度数有三种主要类型:规格化数、非规格化数和伪非规格化数。每种类型代表相邻(但重叠)的数字范围,伪非规格化数最小,然后是非规格化数,最后是规格化数。由于常规 double 的整个范围都包含在规格化长双精度数的范围内,因此非规格化数和伪非规格化数必须四舍五入为零。

长双精度的内部字节包含四个部分(或字段):一个符号位(s),一个带偏置的指数(e),一个尾数位(j)和一个小数部分(f)。双精度数的排列方式类似,只是它们没有尾数位,并且指数和小数部分字段更小。下图说明了这些差异,并显示了字段如何进行转换。

Field layouts and the translation between them.

现在你可能想知道j是什么,以及在转换为 double 时它会发生什么。我发现它无关紧要,只在检测不支持状态时使用,所以这里不再详述。

解决方案

因此,现在的问题可以通过确定长双精度数的类型,然后转换ef(如果它碰巧是规格化数)来解决。

f开始,以下代码将其转换为适合 double 的值。

f >>= 11;

它被移位以适应 double 的小数部分字段中可用的 11 位。接下来,我们转换e

e -= (0x3FFF - 0x3FF);

由于e带偏置,将其从 15 位转换为 11 位需要减去原始偏置(214 - 1)并加上新的偏置(210 - 1)。

显然,在此转换后,e仍然可能超出 11 位数的范围。如果e太高,则该数字太大,无法由 double 表示。在这种情况下,将抛出 OverflowException。如果e小于 0,则该数字太小,将四舍五入为零。

但是,如果e不小于 -51,则可以通过一些仔细的位操作将其转换为非规格化双精度数来挽救。以下代码正是这样做的

if(e >= 0x7FF) //outside the range of a double
    throw new OverflowException();
else if(e < -51) //too small to translate into subnormal
    return 0;
else if(e < 0) //big enough to translate into subnormal
{
    f |= 0x10000000000000;
    f >>= (1 - e);
    e = 0;
}

要理解上述转换,了解规格化 double 的数学表示很重要。以下是一个(简化的)定义。

2e-1023 x 1.f, e > 0

我们将e降低到 1023 以下的程度越多,最终将结果数字除以 2 的次数就越多。在这种情况下,我们试图将e降低到 0 以下,这是不允许的。另一种减少结果的方法是改为将f分量除以 2,即将其按位右移。由于双精度数的尾数部分有 52 位宽,因此在得到零之前,最多可以进行 52 次移位。

过程的最后一步是用我们新的字段值创建一个双精度字节数组,并使用标准的 BitConverter 类将其读取为 double

Using the Code

该代码的使用方式与 System.BitConverter 类相同。要读取长双精度字节数组,请使用 ToDouble() 并指定字节数组和起始索引。要从 double 生成字节数组,请使用 BetBytes() 并指定 double

结论

这个项目证明了对浮点数的结构和位操作进行了有趣的探索。

一旦理解了双精度数的内部结构,构造和解构它们就非常容易了。

参考文献

历史

  • 2004-04-06:初始发布。
© . All rights reserved.