逆向工程未知浮点格式

5
我正在尝试逆向工程某些旧文件格式(Cinema4D旧版本),但找不到规范说明。
在这种文件格式中,我已经发现浮点数值被存储为四个字节,但它们似乎不是正常的IEEE格式,这不是大小端问题。最近我花了很多时间使用十六进制<->浮点数转换工具来解决这个问题。
以下是一些样本值:
0     = 00 00 00 00
1     = 80 00 00 41
2     = 80 00 00 42
4     = 80 00 00 43
8     = 80 00 00 44

0.25  = 80 00 00 3F
16384 = 80 00 00 4F

根据上面两行的观察,从3楼到4楼似乎有些东西环绕在这里。

1.5  = C0 00 00 41
2.5  = A0 00 00 42

-1   = 80 00 00 C1
-1.5 = C0 00 00 C1
-2   = 80 00 00 C2
-3   = C0 00 00 C2

以下是一些观察结果:

  1. 将最后一个字节加1,会使值翻倍
  2. 如果最后一个字节的高位被设置,那么这个数是负数
  3. 第一个字节对于非整数值有影响

虽然有一些明显的模式,还有一些指数/尾数的运算,但我还没有能够找出规律。也许我甚至错过了一些显而易见的东西,它可能是正常的IEEE格式?解决尾数/指数等需要多少比特位不是问题(在上面的例子中,两个中间字节为零),首先我需要找出计算浮点值的公式。


5
可能前三个字节是完整的有效数字(包括前导1)。 - Sneftel
1
@Sneftel:我也是这么认为的。将前三个字节视为在[0.5,1.0)范围内给出的有效数字,最后一个字节的最高位是符号位,剩余的7位给出超额64指数。然而,这与我所知道的任何常见浮点格式都不匹配(不是IEEE 754,不是VAX,不是IBM,不是Cray,...)。 - Mark Dickinson
非常感谢你们两位,这正是它的本意(这可能是在FPU普及之前,也许在包含前导1时在软件中实现更快)。 - user1643428
2
如果在尾数中显示了一个前导1,请注意输入中可能存在非规格化数字的可能性。也就是说,将2表示为40 00 00 43也是可能的。 - Patricia Shanahan
1个回答

2
线索在这里,即Cinema 4D首次亮相于Commodore Amiga平台,该平台使用FFP浮点格式,似乎是为了便于软件仿真而设计的。这在Amiga ROM内核参考手册的第35章中有解释:

尾数被认为是一个二进制定点小数;除了0外,它总是规范化的(尾数被移位并调整指数,以使尾数在最高位具有1位)。因此,它表示小于1但大于或等于1/2的值。

指数是需要正确定位尾数以反映数字的真实算术值所需的二的幂。它以过量-64符号表示法保存,这意味着两个补码值向上调整64,从而将$40 (-64)到$3F (+63)更改为$00到$7F。

0的值被定义为所有32位都为0。

尾数位存储在最高的三个字节中,而最低有效字节由其最高位的符号位和最低有效六位的偏置指数组成。除零外,32位数字x的数值因此为(-1)x<7> * (x<31:8> / 224) * 2(x<6:0> - 64)

基于此,以下ISO-C99代码提供了一个名为decode_ffp()的函数,该函数返回以无符号32位整数形式提供的FFP浮点数的数值。请注意,伪零和非规范化编码的行为未定义,因为官方文档没有说明应如何处理它们。

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

float decode_ffp (uint32_t a)
{
    const uint32_t FFP_EXPO_BIAS = 64;
    const uint32_t FFP_MANT_BITS = 24;
    const uint32_t FFP_EXPO_BITS = 7;
    const uint32_t FFP_EXPO_MASK = (1 << FFP_EXPO_BITS) - 1;
    uint32_t mant = a >> (FFP_EXPO_BITS + 1);
    uint32_t sign = (a >> FFP_EXPO_BITS) & 1;
    int32_t expo = (a & FFP_EXPO_MASK) -  FFP_EXPO_BIAS;
    float val;

    if (a == 0) {
        val = 0.0f;
    } else {
        val = exp2f (expo) * mant / (1 << FFP_MANT_BITS);
        val = (sign) ? (-val) : val;
    }
    return val;
}

int main (void)
{
    uint32_t test_vec[] = {
        0x00000000,
        0x80000041,
        0x80000042,
        0x80000043,
        0x80000044,
        0x8000003F,
        0x8000004F,
        0xC0000041,
        0xA0000042,
        0x800000C1,
        0xC00000C1,
        0x800000C2,
        0xC00000C2
    };
    int num_test_vec = sizeof test_vec / sizeof test_vec[0];

    for (int i = 0; i < num_test_vec; i++) {
        printf ("%08x ==> % 15.8e\n", test_vec[i], decode_ffp (test_vec[i]));
    }
    return EXIT_SUCCESS;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接