什么时候使用标准<比较双打比较安全?在地图/排序?

时间:2012-11-12 19:26:52

标签: c++ floating-point

  

可能重复:
  Floating point keys in std:map

我知道有些情况下你想提供一个特殊的功能来进行双打比较。这种情况可能包括您进行算术运算的次数,因为您在浮点运算中得到了舍入。

然而,有时使用安全<或者>比较双打?

假设我有一次计算的双精度列表,我需要将其排序或用作地图中的一个键(即.std :: map),并且我保证之后没有任何额外的算术运算。

如果使用迭代器遍历集合,我可以保证排序顺序正确吗?

我想我可以通过这种方式获得更多表现。

3 个答案:

答案 0 :(得分:5)

只要您从不添加任何NaN值,使用<来比较double中的std::map值始终是安全的。用于映射的比较必须是严格弱排序,它具有这些要求(C ++03§23.1.2/ 2和§25.3/ 3-4):

  • equiv(a, b)定义为!(a < b) && !(b < a)。然后,<equiv都必须是传递关系:
    1. 如果a < bb < c,那么a < c必须为真
    2. 如果equiv(a, b)equiv(b, c),那么equiv(a, c)必须为真

double值比较肯定满足这些公理,除了,当NaN值被投入混合时。 NaNs有一个奇怪的属性,它们不等于所有值(包括它们自己!)但不低于或大于任何值:

NaN < 0    // false
NaN <= 0   // false
NaN == 0   // false
NaN > 0    // false
NaN >= 0   // false
NaN != 0   // true
NaN < NaN  // false
NaN <= NaN // false
NaN == NaN // false (!!!)
NaN > NaN  // false
NaN >= NaN // false
NaN != NaN // true (!!!)

这会导致问题,因为根据上述规则,NaN具有equiv(x, NaN)对所有x都适用的属性,其传递性意味着所有值都是等效的,除非NaN值显然不相等。

答案 1 :(得分:4)

是的,在集合或地图中使用标准operator<。大多数模糊double比较器都不够严格,无法在mapset中使用。 (并且希望在访问所述地图时你没有弄乱浮点模式......)

我建议您使用multimapmultiset,因为您认为相同的两个值可能略有不同。如果您已经期待多个条目,则应该更好地处理几乎相同的条目。

接下来,在搜索匹配时,请执行lower_bound(x - epsilon)upper_bound(x + epsilon),以便在地图中捕获不在您认为位置的条目。

即,这是“此double”代码中的multiset<double>

typedef std::multiset<double> double_set;
std::pair< double_set::iterator, double_set::iterator >
get_equal_range( double_set& s, double d, double epsilon = 0.00001 )
{
  auto lower = s.lower_bound( d-epsilon );
  auto upper = s.upper_bound( d+epsilon );
  return std::make_pair( lower, upper );
}
std::pair< double_set::const_iterator, double_set::const_iterator >
get_equal_range( double_set const& s, double d, double epsilon = 0.00001 )
{
  auto lower = s.lower_bound( d-epsilon );
  auto upper = s.upper_bound( d+epsilon );
  return std::make_pair( lower, upper );
}
bool TestMembership( double_set const& s, double d, double epsilon = 0.00001 )
{
  auto range = get_equal_range( s, d, epsilon );
  return range.first != range.second;
}

因此,给定的double可以映射到一系列条目,或者set中有超过1个匹配。

从以下一个很好的答案中被盗:如果您通过operator<,默认的双map会将setNaN搞定。

将内容放入mapset(以及多个版本)时,必须维护严格的弱排序规则。如果你失败了,你会得到看似随机的崩溃,偶尔会出现无限循环:mapset中的代码对于排序失败并不健全。

答案 2 :(得分:3)

使用标准'&lt;'在地图或集合中是至关重要的,因为您尝试引入的任何软糖因素都会破坏他们用来组织容器的严格排序。不幸的是,这意味着您可能无法找到元素,除非您具有确切的值。

您可以使用lower_boundupper_bound,使其值低于或高于您想要的搜索值,以生成值必须为的范围。