在 IEEE、IBM 或 VAX 浮点数格式和字节表达式之间转换






4.74/5 (13投票s)
2005年11月22日
6分钟阅读

110765

1936
该程序可以在 IEEE、IBM 或 VAX 浮点数格式及其字节表示之间进行转换。
引言
该程序可以将浮点数转换为其字节表示,或将字节表示转换为浮点数。
背景
您是否曾尝试开发一个程序来读取 DLIS(数字测井标准)格式的数据文件?我发现示例测井数据是以 VAX 单精度浮点格式记录的。因此,我需要从二进制文件中读取 4 字节流,然后恢复实数。我成功地恢复了所有通道的所有帧数据。我将我的结果与 Schlumberger 的免费工具程序 Toolbox 的输出进行了比较。它们完全相同。我还使用了一个免费的 Java 包 Cynosurex 进行了一些测试,结果也一样。这让我想起了五年前我做的一些关于浮点数和字节顺序分析的半成品工作,并激发了我继续前进的热情。
浮点数的位表示
IEEE 单精度浮点数
SEF : S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits : 1 2 9 10 32
bytes : byte1 byte2 byte3 byte4
IEEE 双精度浮点数
SEF: S EEEEEEE EEEE FFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits: 1 2 12 13 64
bytes: byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
frctn.: L1 L2
IBM 单精度浮点数
SEF : S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF
bits : 1 2 8 9 32
bytes : byte1 byte2 byte3 byte4
IBM 双精度浮点数
SEF: S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits: 1 2 8 9 64
bytes: byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
frctn.: L1 L2
VAX 单精度浮点数
SEF : S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits : 1 2 9 10 32
bytes : byte2 byte1 byte4 byte3
浮点数的通用编码公式
V = (-1)S * M * A( E - B )
M = C + F
V 是值,S 是符号,M 称为尾数,A 是基数,E 是指数,B 是指数偏移量,C 是尾数常数,F 是分数。A、B 和 C 是常量,它们可能因浮点数架构而异。以下是一些示例:
IEEE single float : A = 2 B = 127 C = 1
IEEE double float : A = 2 B = 1023 C = 1
IBM single float : A = 16 B = 64 C = 0
IBM double float : A = 16 B = 64 C = 0
VAX single float : A = 2 B = 128 C = 0.5
分数的最大值
如上所述,F 是分数。IEEE 和 VAX 分数 F 的最小值是 0,而 IBM 分数的最小值是 1/16。F 为零表示所有分数位(上面位表示中的 F)都为 0。当所有分数位都为 1 时,将达到分数的最大值。为了计算出它,我们必须使用一点高中数学,您还记得这个公式吗?
1/2 + 1/4 + 1/8 + ... + 1/2n = 1 - 1/2n
这里唯一容易被忽略的细节是 VAX 单精度浮点数。除了其奇怪的字节顺序外,其分数位段从 1/4 开始,而不是像 IEEE 或 IBM 那样从 1/2 开始。这是复杂性总是源于个性的另一个例子。
G is the maximum value of the fraction F
IEEE single float : G = 1 - 1/223
IEEE double float : G = 1 - 1/252
IBM single float : G = 1 - 1/224
IBM double float : G = 1 - 1/256
VAX single float : G = 1 - 1/224 - 1/2
尾数范围
根据上述 C 和 G 的值,很容易计算出尾数范围。IBM 浮点数尾数的最小值将在下面解释。
IEEE single float : 1 <= M <= 2 - 1/223
IEEE double float : 1 <= M <= 2 - 1/252
IBM single float : 1/16 <= M <= 1 - 1/224
IBM double float : 1/16 <= M <= 1 - 1/256
VAX single float : 1/2 <= M <= 1 - 1/224
字节顺序
我使用一个简单的联合数据结构和一个两字节无符号短整数 258 来找出您的内存存储数字的字节顺序类型。对于 Little Endian 架构,例如 Intel,此函数将返回 2;对于 Big Endian 架构,例如 SPARC,此函数将返回 1。
字节转换为浮点数
转换有两个步骤。第一步是将字节转换为 SEF,即符号、指数和分数。第二步是将 SEF 转换为浮点数。
1. 字节到 SEF
首先,调整传入的字节顺序以适应上述浮点数的位表示,然后可以通过基于上述位表示的一些位运算来获得 SEF 值。对于双精度浮点数,我将分数分解为两部分:两个无符号长整数,即 L1 和 L2。
2. SEF 到浮点数
根据上述通用编码公式和三个常量 A、B 和 C,很容易从 SEF 恢复浮点数。
浮点数转换为字节
与上述方法类似,转换有两个步骤。第一步是将浮点数转换为 SEF。第二步是将 SEF 转换为字节。
1. 浮点数到 SEF
这是所有程序中最重要的部分。我开发了两种方法来计算浮点数的 E 和 F。
第一种方法更自然。其原理与将整数转换为二进制表示相同,通过连续除以基数 2 来获取每一位。在我们的情况下,我们可以连续除法或乘法基数,直到商落入上述尾数范围。选择除法还是乘法取决于 E-B 的值是正数还是负数,但在不知道 E 之前,无法知道 E-B 的符号。实际上,我们可以通过将浮点数与尾数边界值进行比较来确定它。循环次数用于确定 E 值,同时,循环后原始浮点数的余数用于确定 F 值。
第二种方法稍微复杂一些。它使用反向算法通过对数来计算 E 值,然后很容易根据上述编码公式得到 F 值。实际上,我们可以得出以下 E 和 F 的公式:
V is the floating point number
D = log2 , base is e
IEEE single float : E = (int) ( logV / D + B )
IEEE double float : E = (int) ( logV / D + B )
IBM single float : E = (int) ( ( logV / D ) / 4 + 1 + B )
IBM double float : E = (int) ( ( logV / D ) / 4 + 1 + B )
VAX single float : E = (int) ( ( logV / D ) + 1 + B )
F = V / A(E-B) - C
我将对这些公式进行简要证明。零值和符号 S 可以忽略不计,因此假设浮点值为正:V > 0。
- IEEE 浮点数
IEEE 浮点数的尾数范围是:1 <= M < 2。因此:0 <= logM / log2 < 1。注意:E 是非负整数,所以
(int) ( logV / log2 + B ) = (int) ( logM / log2 + ( E-B ) + B ) = (int) ( logM / log2 + E ) = E
- IBM 浮点数
这里的关键点在 RP66 参考文档中提到:*字节 2 的位 1-4 可能不全是零,但真正的零除外。换句话说,尾数的第一个十六进制数字必须是非零的,真正的零除外*。这意味着 IBM 浮点数尾数的最小值是 1/16。因此:0 < logM / log16 + 1 < 1。注意:E 是非负整数。
(int) ( ( logV / log2 ) / 4 + 1 + B ) = (int) ( logM / log16 + ( E-B ) + 1 + B ) = (int) ( logM / log16 + 1 + E ) = E
- VAX 浮点数
VAX 浮点数尾数的最小值是 1/2,因此:0 < logM / log2 + 1 < 1。注意:E 是非负整数。
(int) ( logV / log2 + 1 + B ) = (int) ( logM / log2 + ( E-B ) + 1 + B ) = (int) ( logM / log2 + 1 + E ) = E
2. SEF 到字节
这部分没有什么难点。只需要进行字节顺序调整和一些位操作。
该程序还包括一个常规的联合方法来转换 IEEE 浮点数及其字节表示。
编译
该程序在 Windows-XP 的 MinGW 环境下编译。您必须设置 PATH 环境变量,或者在编译前运行 *setp.bat*。
set PATH = %PATH%; C:\MinGW\bin ;
然后运行 *clib.bat* 创建库,或者手动编译程序如下:
del libNumber.lib
del *.o
g++ -c ByteOrder.c
g++ -c Float2SEF.c
g++ -c SEF2Byte.c
g++ -c Byte2SEF.c
g++ -c SEF2Float.c
g++ -c IeeeFloat.c
g++ -c IbmFloat.c
g++ -c VaxFloat.c
g++ -c TestlibNumber.c
ar m libNumber.lib
ar r libNumber.lib *.o
ar t libNumber.lib
del *.o
运行 *cpsam.bat* 编译两个测试程序如下:
cpsam test1
cpsam test2
最后,您可以在 DOS 窗口中运行 *test1.exe* 程序。您也可以将输出重定向到一个文本文件,如下所示:
test1 > test.txt
*test2.exe* 是另一个测试程序,用于测试该库的任何浮点数。
参考
我已将所有参考网页都打包到我的源代码 Zip 包中。