有一个以下函数可以在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;
}
有人可以解释它是如何起作用的吗?
答案 0 :(得分:5)
此代码利用了IEEE 754格式的浮点数结构。结构本身专门为此类操作而设计,以便快速进行比较操作。
每个单精度IEEE 754号码有三个部分(从MSB到LSB的顺序):
f1
大于f2
f1
为正,f2
为否定f1
和f2
均为正数但f1
指数高于f2
f1
和f2
都是正数且具有相同的指数,但f1
的有效数大于f2
f1
和f2
为负,则如果它们在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
为肯定,那么t1
为0
而(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都有专门的指令来执行硬件中的签名比较。