双精度浮点数比较

8

我有点困惑- 当double类型被存储为不透明(二进制)字段时,它们的比较是否仍然能够正确进行?我面临的问题是double类型包含一个用于表示正负的前导位,当它们以二进制数据形式存储时,我不确定它们是否会被正确比较:

enter image description here

我希望确保比较功能能够正确运行,因为我将双精度浮点数作为key tuple (e.g. ) in LevelDB的一部分使用,并且希望对正数和负数保持数据局部性。 LevelDB只使用不透明字段作为键,但它确实允许用户指定自己的比较器。 但是,我只想确保除非绝对必要,否则不会指定比较器:
// Three-way comparison function:
//   if a < b: negative result
//   if a > b: positive result
//   else: zero result
inline int Compare(const unsigned char* a, const unsigned char* b) const 
{
    if (*(double*)a < *(double*)b) return -1;
    if (*(double*)a > *(double*)b) return +1;
    return 0;
}

1
两件事情:如果两个参数都是NAN,那么这将失败,因为NAN!= NAN。另一件事是要注意对齐方式。并非所有系统都支持未对齐访问。 - Mysticial
@Mysticial,感谢您的反馈...我会尝试弄清楚如何处理NAN。那么我的假设是正确的吗?我必须为包含双精度浮点数的键指定比较运算符吗? - Kiril
你为什么要使用 const unsigned char * 而不是 const void * - Chris Lutz
@Link - 我错过了C++标签,你的代码并没有很好地体现出C++的特性,所以我假设这应该是一个比较函数,可以传递给qsort或类似的函数。 - Chris Lutz
@ChrisLutz 没问题...比较函数是在Google的LevelDB中传递的,它用于按键对记录进行排序。对我来说,拥有一个良好的比较函数(既快速又正确)非常重要,因为我将拥有数百万条记录,并且该函数可能每秒被调用100k-200k次(很可能会这样)。 - Kiril
显示剩余3条评论
3个回答

3
使我的评论成为答案。
有两件事可能会出错:
1. 如果任一(或两个)参数是NAN,则比较将始终返回false。因此,即使二进制表示相同,NAN == NAN也始终为false。此外,它违反了比较传递性。
2. 如果任一参数未正确对齐(因为它们是char指针),则在不支持错误对齐内存访问的机器上可能会遇到问题。对于那些支持这样做的机器,您可能会遇到性能损失。
因此,为了解决这个问题,您需要添加一个陷阱情况,如果任何一个参数变成NAN,则会调用该情况。 (我不确定INF的状态。)
由于需要这个陷阱情况,您需要定义自己的比较运算符。

1
@Mystical:您能详细说明第二点吗?具体是哪些机器,预计会有多大的性能损失? - Xander Tulip
访问内存中的多字节单词(例如8字节的double)时,地址应该被单词大小整除。当您正常使用指针时,编译器会确保这一点。然而,当您开始进行指针转换时,一个类型(char)的对齐可能不足以满足另一个类型(double)。在不支持不对齐访问的机器上,硬件将抛出异常(崩溃)。在支持它的机器上,通常会有一个惩罚,因为硬件需要进行两个单独的访问,然后将它们组合并提取相关部分。 - Mysticial
@mystical 我无法控制键/值的强制转换方式。Google的LevelDB只接受指向类型为char的指针的键/值,如果我的键最初是double或64位unsigned整数类型,则必须将它们强制转换为char*(这是我主要使用的两种键类型)。LevelDB只是一个键/值嵌入式数据库,所以我不确定对齐的影响以及我能做些什么。 - Kiril
如果您的“double”指针最初被转换为“char *”,然后在函数中再次转换为“double *”,则如果指针值仍然相同,则对齐将是正确的。因为听起来您只是为了绕过语法而进行转换。 - Mysticial
看文档,如果这将在x86/x64上运行,最好忽略对齐问题。任何重新对齐数据的步骤都可能比让硬件处理不对齐更昂贵。此外,在对齐的情况下不会增加任何开销。 - Mysticial
显示剩余2条评论

1

是的,您必须指定自己的比较函数。这是因为双精度浮点数不一定存储为“大端”值。即使在逻辑上它在幂之前出现,但指数在内存中不会驻留在幂之前,当该值以大端格式写出时。

当然,如果您在同一数据库中在不同的CPU架构之间共享数据,由于将数据存储为二进制块,您可能会遇到奇怪的字节序问题。

最后,即使您可以控制字节序,我仍然不会信任它。例如,如果一个双精度浮点数未被规范化,则在将其作为二进制数据进行比较时,它可能无法正确比较到另一个双精度浮点数。

当然,其他人提到的有关对齐和NAN和INF等奇怪值的所有内容都很重要,需要在编写比较函数时注意。但是,就是否应该编写比较函数而言,我必须说这是一个非常好的想法。


1

我假设你的数字格式符合IEEE 754标准。如果是这样,那么简单的有符号整数比较是行不通的--如果两个数字都是负数,比较的结果会被颠倒。因此,你必须提供自己的比较器。


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