C++ 双精度浮点比较
我在这里有点困惑-当双精度存储为不透明(二进制)字段时,双精度的比较仍然正确吗?我面临的问题是,double包含符号的前导位(即正或负),当它们存储为二进制数据时,我不确定是否会正确比较: 我希望确保比较能够正确进行,因为我使用double作为a的一部分,并且我希望保留正数和负数的数据局部性。LevelDB只使用不透明字段作为键,但它允许用户指定自己的比较器。但是,我只想确保不指定比较器,除非我绝对需要:C++ 双精度浮点比较,c++,comparison,floating-point,double,leveldb,C++,Comparison,Floating Point,Double,Leveldb,我在这里有点困惑-当双精度存储为不透明(二进制)字段时,双精度的比较仍然正确吗?我面临的问题是,double包含符号的前导位(即正或负),当它们存储为二进制数据时,我不确定是否会正确比较: 我希望确保比较能够正确进行,因为我使用double作为a的一部分,并且我希望保留正数和负数的数据局部性。LevelDB只使用不透明字段作为键,但它允许用户指定自己的比较器。但是,我只想确保不指定比较器,除非我绝对需要: // Three-way comparison function: // if a
// 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;
}
//三向比较函数:
//如果ab:阳性结果
//否则:结果为零
内联整数比较(常量无符号字符*a,常量无符号字符*b)常量
{
如果(*(双*)a<*(双*)b)返回-1;
如果(*(双*)a>*(双*)b)返回+1;
返回0;
}
回答我的评论
有两件事可能会出错:
NAN
,则比较将始终返回false。因此,即使二进制表示形式相同,NAN==NAN
也将始终为false。此外,它违反了比较传递性NAN
,那么将调用它。(我不确定INF
的状态)
由于需要此陷阱,您需要定义自己的比较运算符。是的,您必须指定自己的比较函数。这是因为double不一定存储为“big-endian”值。指数将不会驻留在尾数之前的内存中,即使在逻辑上,当值以big-endian格式写出时,它出现在尾数之前 当然,如果您在同一数据库中的不同CPU架构之间共享数据,那么最终可能会出现奇怪的endian问题,因为您将数据存储为二进制blob 最后,即使你能控制endianness,我仍然不相信它。例如,如果一个double未规范化,则当作为二进制数据进行比较时,它可能无法正确地与另一个double进行比较
当然,在编写比较函数时,其他人所说的关于对齐和诸如NAN和INF之类的奇数值的所有内容都很重要。但是,至于你是否应该写一个,我不得不说这将是一个非常好的主意。我假设你的数字格式符合IEEE 754标准。如果是这种情况,那么简单的有符号整数比较将不起作用——如果两个数字都是负数,则比较的结果是相反的。因此,您必须提供自己的比较器。有两件事:如果两个参数都是
NAN
,这将失败,因为NAN!=NAN
。另一件事是要注意对齐。并非所有系统都支持错位访问。@神秘,感谢您的反馈。。。我将尝试找出如何处理NAN
。那么我的假设是正确的?我必须为包含双精度的键指定一个比较运算符?嗯,我对leveldb一点也不熟悉。所以我不理解一半的问题,所以我没有把它作为一个答案。但是是的,如果你需要它返回true,如果它们在二进制中是相同的,那么你必须为NAN添加陷阱案例。此外,比较NAN
s是不可传递的,因此您也必须处理它。为什么要使用const unsigned char*
而不是const void*
?只要将双精度值作为双精度进行比较,表示(符号位等)就不相关。南是一个问题。对齐也可以,但如果a
和b
被初始化为转换的指针,指向正确对齐的double
对象。@Mystical:请详细说明第2点。具体是哪台机器,以及预期的性能影响有多大?当从内存访问多字节字(如8字节双精度)时,地址应该可以被字的大小整除。当您正常使用指针时,编译器会确保这一点。但是,当您开始执行指针强制转换时,一种类型(char
)的对齐方式可能不适合另一种类型(double
)。在不支持未对齐访问的计算机上,硬件将引发异常。(崩溃)在支持它的机器上,通常会有一个惩罚,因为硬件需要进行两次单独的访问,然后将它们合并并提取相关部分。由于我超过500个字符,因此继续:x86/x64将允许对除SIMD向量加载/存储外的所有内容进行未对齐的内存访问。对于这些,有一个单独的未对齐加载/存储指令。在许多RISC架构(如ARM、安腾等)上,需要对齐。在x86/x64的情况下,未对齐访问的性能影响很大程度上取决于体系结构,可以是缓存线拆分的50个周期(在Core 2上)到几乎空闲的(在Sandy Bridge上)。@mystical我无法控制键/值的转换方式。Google的LevelDB只接受指向类型char
的指针的键/值,如果我的键最初是类型double
或64位无符号
整数,那么我必须将它们转换为char*
(这是我主要使用的两种键)。LevelDB只是一个键/值嵌入式数据库,所以我不确定对齐会产生什么影响,我也不确定