解释 Intel 80 位长双字节数组






3.57/5 (20投票s)
2004 年 4 月 6 日
4分钟阅读

54599

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)。双精度数的排列方式类似,只是它们没有尾数位,并且指数和小数部分字段更小。下图说明了这些差异,并显示了字段如何进行转换。
现在你可能想知道j是什么,以及在转换为 double
时它会发生什么。我发现它无关紧要,只在检测不支持状态时使用,所以这里不再详述。
解决方案
因此,现在的问题可以通过确定长双精度数的类型,然后转换e和f(如果它碰巧是规格化数)来解决。
从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:初始发布。