比较浮点数的二进制表示

8

假设我想要一个函数,它接受两个浮点数(xy),并且我想使用它们的32位无符号整数比较它们,而不是使用它们的float表示。也就是说,像-495.5这样的数字具有位表示形式0b110000111110010111000000000000000xC3E5C000作为float,而我有一个具有相同位表示形式的unsigned int(对应于十进制值3286614016,但我不关心它)。是否有一种简单的方法让我只使用它们各自的unsigned int副本中包含的信息来执行像<=这样的操作?


您可以按照cpalmer在下面提到的那样操作,但请注意,IEEE floats的无符号整数表示方式与它们的等效浮点值排序方式不同。例如,当它们作为无符号整数进行比较时,-0.0与0.0并不相同。 - Richard Pennington
你打错字了:应该是-459.5,而不是-495.5 - jfs
4个回答

4
除非您确保所有原始值都为正,否则必须进行有符号比较。您必须使用与原始浮点类型大小相同的整数类型。每个芯片可能具有不同的内部格式,因此将来自不同芯片的值作为整数进行比较最有可能会产生误导性结果。
大多数浮点格式看起来像这样:sxxxmmmm s是符号位
xxx是指数
mmmm是尾数
表示的值将类似于:1mmm << (xxx-k) 1mmm,因为除非该值为零,否则会有一个隐含的前导1位。
如果xxx < k,那么它将是右移。 k接近但不等于可以由xxx表示的最大值的一半。它根据尾数的大小进行调整。
总之,忽略NaN,将浮点值作为相同大小的有符号整数进行比较将产生有意义的结果。设计它们的方式是使浮点比较不比整数比较更昂贵。有编译器优化以关闭NaN检查,以便如果芯片的浮点格式支持,则比较是直接的整数比较。
作为整数,NaN大于无穷大,无穷大大于有限值。如果尝试进行无符号比较,则所有负值都将大于正值,就像将有符号整数强制转换为无符号整数一样。

我同意。我使用无符号值和符号位一起编写了一些测试,虽然有点丑陋,但完全可行。 - sczizzo
1
有符号整数比较通常与浮点数比较负数时不会给出相同的顺序。大多数C和C++实现使用二进制补码,其中表示的负值与相同位的无符号解释相对于其他负值具有相同的顺序。大多数浮点表示使用符号-幅度表示法,其中负值相对于它们的无符号整数解释具有相反的顺序。 - Eric Postpischil

3

如果您真的不关心转换结果,那并不难。但结果极不便携,并且您几乎肯定无法获得与直接比较浮点数完全相似的顺序。

typedef unsigned int TypeWithSameSizeAsFloat; //Fix this for your platform

bool compare1(float one, float two)
    union Convert {
        float f;
        TypeWithSameSizeAsFloat i;
    }
    Convert lhs, rhs;
    lhs.f = one;
    rhs.f = two;
    return lhs.i < rhs.i;
}

bool compare2(float one, float two) {
    return reinterpret_cast<TypeWithSameSizeAsFloat&>(one) 
         < reinterpret_cast<TypeWithSameSizeAsFloat&>(two);
}

只要了解注意事项,并仔细选择第二种类型即可。无论如何,这几乎是毫无价值的练习。

在C语言中使用reinterpret_cast很困难。 - Richard Pennington
@Richard Pennington:然后使用C风格的转换。但是默认写法应该是正确的转换,而不是遗留的转换,只要可能的话,因为C风格的转换已经被弃用。(除非你绝对需要C兼容性) - Billy ONeal
@Richard Pennington:如果作者希望在不支持安全转换的语言中使用代码,那么我认为将其转换回C风格转换并不太难。当标记为C++时,C++风格转换应该是默认列表,因为任何C++都可以转换为C风格转换。 - Billy ONeal
1
@Billy,他实际上是在问如何做一件可能无法达到他期望的事情。使用什么类型转换都无关紧要。 - Richard Pennington
1
走开,不必要的打字!reinterpret_cast<TypeWithSameSizeAsFloat&>(one) < reinterpret_cast<TypeWithSameSizeAsFloat&>(two)。(嘘,你需要在convert2中使用onetwo。) - GManNickG
显示剩余5条评论

2
也许我理解问题有误,但我认为你可以这样做:
bool compare(float a, float b)
{
    return *((unsigned int*)&a) < *((unsigned int*)&b);
}

但是这种做法包含了各种假设,也引发了一个问题:为什么你想要比较两个浮点数的位表示呢?


啊,那个古老的指针强制转换黑科技。丑陋,但威力无穷。 - R. Martinho Fernandes
2
如果你一定要这样做,至少使用reinterpret_cast将其标记为平台特定的hack。 - Billy ONeal
@BillyONeal - 这个问题被标记为C,C++和Objective-C。reinterpret_cast只适用于C++。 - R Samuel Klatchko
@R Samuel Klatchko:在Dennis Zickefoose的回答中,我们已经深入讨论了这个问题。我认为即使存在这样的标签,也应该使用C ++版本,因为从C++转换到C风格的强制转换很容易,但反过来不一定总是可行的。 - Billy ONeal

2
总之,不行。IEEE 754可能允许一些类似这样的黑科技,但它们并不总是有效且处理不了所有情况,而且一些平台不使用该浮点数标准(例如x87上的双精度内部具有80位精度)。
如果您出于性能原因而这样做,我建议您强烈重新考虑--如果使用整数比较更快,编译器可能会自动为您执行,如果不是,则需要多次进行浮点转换,而可能可以通过简单的比较实现而无需将浮点数移出寄存器。

马丁霍:在浮点数单元被集成到x86芯片之前,它们有类似8087、80287和80387的名称。因此,我们称FPU指令为x87。 - Gabe
我有这样的印象,他在整理而不是查找NaN,所以我认为他会没问题的。 - Gabe
@gabe:除非它们已经在FPU寄存器中,否则不会这样。有人在比较两个随机的内存浮点数的可能性很小——他们很可能之前已经在处理至少一个浮点数了。 - Billy ONeal
@Billy ONeal:如果你正在对一组数字进行排序,它们不仅仅是随机的内存浮点数。 - Gabe
2
重要的是要明白,这种比较可能会或可能不会带来性能上的好处:浮点数比较本身就非常快,而整数比较的设置可能会带来额外的开销。再加上比较不具备可移植性,在某些情况下无法使用[即使你愿意忽略NaN和正负无穷大,正负零也很容易出错],因此你最好相信编译器,除非有充分的理由不这样做。 - Dennis Zickefoose
显示剩余7条评论

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