是否可以为Set <double> .contains()设置精度?

时间:2018-09-01 11:19:09

标签: java set double precision

假设我们有Set<Double>的实现。它包含以下值:[2.0, 5.0, 7.0]

contains(2.0001d)在这种情况下返回false,因为double的值是通过完全匹配进行比较的。

是否可以为boolean contains(Object o)方法设置一些双精度?

如果不可能,除了将值存储在顺序集合中,对其进行迭代并比较每个值之外,您建议什么解决方法?

4 个答案:

答案 0 :(得分:6)

Set.contains有一个基于等式的精确定义:

  

更正式地讲,当且仅当该集合包含元素true时返回(o==null ? e==null : o.equals(e))

它使用除平等之外的任何东西都违反了该方法的契约。平等有一个精确的定义,说它必须是可传递的(在其他属性中)。使用容差的相等方法不是可传递的。

因此,Set.contains无法允许公差。

但是,这并不是说您不应该检查集合中是否包含某个值的公差范围内的值-请勿尝试超载contains的概念来做到这一点

例如,您可以有一个采用NavigableSet(例如TreeSet)并使用其subSet method的方法:

static boolean containsApprox(NavigableSet<Double> set, double target, double eps) {
  return !set.subSet(target - eps, true, target + eps, true).isEmpty();
}

这仅请求集合中从target-epstarget+eps(包括true参数所指示)的部分。如果这是非空的,则eps的{​​{1}}中的集合中有一个值。

这显然是与标准target分开的概念,因此可以执行包含不共享相同属性的包含检查。

您无法对Set.contains进行同样的subSet技巧,因为它是无序映射-没有有效的方法来提取给定范围内的值。您必须像Sun's answer中那样遍历整个集合,以寻找匹配的值。

答案 1 :(得分:3)

例如,可以使用anyMatch,根据.之后的前两位进行比较:

Set<Double> set = Set.of(2.0, 5.0, 7.0);

Double compared = 2.0001d;

System.out.println(
        set.stream().anyMatch(aDouble -> 
                Math.floor(aDouble * 100) / 100 == Math.floor(compared * 100) / 100
));

答案 2 :(得分:2)

我可以看到三个选项:

  • 将数字四舍五入后再添加到集合中
  • 编写一个double包装类并重新定义equals方法
  • 使用TreeSet和自定义比较器

请注意,从填充集合的顺序会影响保留元素的角度来看,后两个选项可能令人满意,也可能不令人满意。例如,如果将精度设置为0.01,然后再加上0.01,然后再加上0.011,然后再加上0.02,则该组中将有两个元素(0.01和0.02)。如果先加上0.011,再加上0.01,然后再加上0.02,那么您将只有一个元素:0.011。我不知道您的用例是否有问题。

最后一个选项可能看起来像这样:

static Set<Double> setWithPrecision(double epsilon) {
  return new TreeSet<> ((d1, d2) -> {
    if (d1 <= d2 - epsilon) return -1;
    if (d1 >= d2 + epsilon) return 1;
    return 0;
  });
}

示例用法:

Set<Double> set = setWithPrecision(0.01);
set.add(0d);
set.add(0.00001d);
set.add(0.01d);
set.add(0.02d);

System.out.println(set); // [0.0, 0.01, 0.02]

答案 3 :(得分:0)

假设您的HashSet#contains调用点在某种程度上可以从某个地方接收到Double的方法中,则可以简单地设置Double的精度。尽管Double不提供开箱即用的Double对象四舍五入的方式,但是您可以很好地创建一个四舍五入的助手,该助手将接受您想要达到的精度并返回四舍五入的数字。

例如:

public static Double roundToPrecision(Double number, int precision) {
    return BigDecimal.valueOf(number)
        .setScale(precision, RoundingMode.DOWN)
        .doubleValue();
}

传递2.0001d会有效地导致这一过程转向2.0