1

有一个以下函数应该在 2 个浮点值之间进行比较,但在某些特定情况下比常规比较更快(例如在 Cortex-A8 上)

int isGreater(float* f1, float* f2)
{
    int i1, i2, t1, t2;

    i1 = *(int*)f1; // reading float as integer
    i2 = *(int*)f2; // reading float as integer

    t1 = i1 >> 31;
    i1 = (i1 ^ t1) + (t1 & 0x80000001);

    t2 = i2 >> 31;
    i2 = (i2 ^ t2) + (t2 & 0x80000001);

    return i1 > i2;
}

有人可以解释它是如何工作的吗?

4

1 回答 1

5

此代码利用 IEEE 754 格式的结构来处理浮点数。该结构本身是专门为此类操作设计的,以便快速进行比较操作。

每个单精度 IEEE 754 数字包含三个部分(从 MSB 到 LSB 的顺序):

  • 符号位
  • 指数部分(8 位)
  • 尾数的有效位(23 位)

f1大于f2如果:

  • f1是正的和f2负的
  • f1并且f2都是正数,但f1指数大于f2
  • f1并且f2都是正数,并且具有相同的指数,但f1有效数大于f2
  • f1如果和f2为负,则与前两个相反

如果它们是二进制补码表示,则可以将两个浮点数作为整数进行比较。不幸的是,IEEE 754 不使用二进制补码来表示负数,这就是为什么此代码执行转换以便能够将数字作为有符号整数进行比较。

以下是对每行代码的作用的逐步评论:

i1 = *(int*)f1; // reading float as integer
i2 = *(int*)f2; // reading float as integer

这个使用了在大多数 32 位系统上将sizeof(int) == sizeof(float)浮点数读入常规有符号整数变量的事实。

t1 = i1 >> 31;

这个提取 的符号位f1。如果f1为负,则其 MSB 为负1,因此i1为负。将其向右移动 31 位会保留符号,因此如果i1为负数t1,则所有位都会设置为1(等于 -1)。如果f1为正,则其符号位将为0并且最终t1将等于0

i1 = (i1 ^ t1) + (t1 & 0x80000001);

如果符号位是负数,则1此行将执行转换为二进制补码表示。f1

以下是它的工作原理:如果f1是正数,那么t10并且(i1 ^ t1)将只是i1并且(t1 & 0x80000001)将是0并且最终i1将保持其原始值。如果f1为负,则t1所有位都设置为1,RHS 上的第一个表达式将是 的位反转,i1第二个表达式将等于0x80000001。这种方式i1将被转换为它的位反转并被1添加。但这将导致一个正数,因为 MSB 将被清除,这就是为什么0x80000000还要添加以保持该数字为负数。

t2 = i2 >> 31;
i2 = (i2 ^ t2) + (t2 & 0x80000001);

执行与上述相同的操作f2

return i1 > i2;

只需比较两个生成的有符号整数。大多数 CPU 都有专门的指令来在硬件中执行带符号的比较。

于 2012-06-19T09:23:00.200 回答