C代码说明

时间:2012-06-19 08:19:27

标签: c

有一个以下函数可以在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;
}

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

1 个答案:

答案 0 :(得分:5)

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

每个单精度IEEE 754号码有三个部分(从MSB到LSB的顺序):

  • 签署位
  • 指数部分(8位)
  • 尾数的有效数(23位)
如果符合以下情况,

f1大于f2

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

如果它们在two's complement表示中,则可以将两个浮点数作为整数进行比较。不幸的是,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 {1}}。这种方式i1将转换为其位反转,并添加1。但这会导致正数,因为MSB将被清除,这就是为什么0x80000000也被添加以保持数字为负数。

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

f2执行与上述相同的操作。

return i1 > i2;

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