可靠地使用double作为std::map键

Reliably using doubles as std::map keys

本文关键字:std map 作为 double      更新时间:2023-10-16

我的一个同事最近提出了一个有趣的技巧,可以可靠地将浮点数用作c++中的std::map之类的键。

假设您想对一些浮点值(如price)进行大小写处理,并且您知道这些值只能采用离散值,尽管表示实数(例如,以特定ticksize的间隔),那么以下代码片段可靠地将输入price转换为long long价格:

double price, ticksize; // Initialized elsewhere
long long priceKey = 0;
if ((price / ticksize) < (ticksize / 2)) {
    priceKey = (long long) (price / ticksize);
} else {
    priceKey = (long long) ((price / ticksize) + (ticksize / 2));
}

例如,如果price = 98.05ticksize = 0.05,那么我们最终得到以下结果:

price / ticksize = 1960.9999999999998
ticksize / 2 = 0.025
priceKey = (long long) 1960.9999999999998 + 0.025 = 1961

priceKey可以继续在std::map<long long, order_t>中使用,以可靠地检索特定价格水平的订单。

是否存在这种逻辑会失败的情况?我试着为自己找出一个证明,为什么这可以工作,但我认为我没有足够的经验来解释浮点运算

首先,您不应该除以ticksize,而应该乘以ticksize的倒数,考虑到您所描述的应用程序,它可能完全可以表示为double。在您的示例中,此逆将是20.0

第二,您可以使转换稍微简单一点,并且在我看来更易于阅读:

price乘以ticksize的倒数后,从双精度四舍五入到最接近的整数,作为long long(函数llround)或double(函数nearbyint)。只要使用兼容的哈希和等式函数(哈希函数应该为+0返回相同的哈希值),就没有内在的理由不应该使用double作为std::map的键。和0。如果相等性是==,如果你使用==作为相等性,可能你不应该使用NaN作为键)。

在代码:

priceKey = llround(price * inverseticksize);