Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中,如何测试'Double'列表是否包含特定值_Java_Floating Point - Fatal编程技术网

在Java中,如何测试'Double'列表是否包含特定值

在Java中,如何测试'Double'列表是否包含特定值,java,floating-point,Java,Floating Point,背景:浮点数有舍入问题,因此永远不要将其与“=”进行比较 问:在Java中,如何测试Double列表是否包含特定值我知道各种解决方法,但我正在寻找最优雅的解决方案,可能是使用Java或第三方库功能的解决方案 import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { // should be 1.38, bu

背景:浮点数有舍入问题,因此永远不要将其与“=”进行比较

问:在Java中,如何测试
Double
列表是否包含特定值我知道各种解决方法,但我正在寻找最优雅的解决方案,可能是使用Java或第三方库功能的解决方案

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // should be 1.38, but end up with 1.3800000000000001
        Double d1 = new Double(1.37 + 0.01);
        System.out.println("d1=" + d1);

        // won't be able to test the element for containment
        List<Double> list = new ArrayList<Double>();
        list.add(d1);
        System.out.println(list.contains(1.38));
    }
}

谢谢。

一般的解决方案是编写一个实用方法,在列表中循环并检查每个元素是否在目标值的某个阈值内。不过,我们可以在Java 8中做得稍微好一点,使用:

list.stream().anyMatch(d->(Math.abs(d/d1-1)
请注意,我使用的是建议的相等测试

如果您不使用Java 8,我将按照以下思路编写一个简单的实用程序方法:

public static boolean contains(Collection<Double> collection, double key) {
    for (double d : collection) {
        if (Math.abs(d/key - 1) < threshold)
            return true;
    }
    return false;
}
公共静态布尔包含(集合集合,双键){
用于(双d:收集){
if(数学abs(d/键-1)<阈值)
返回true;
}
返回false;
}

注意您可能需要在这两种方法中添加一个特例,以检查列表是否包含
0
(或使用
abs(x-y)
方法)。这只需要在相等条件的末尾添加
| |(abs(x)

您可以将Double包装到另一个类中,该类为其equals方法提供了“足够接近”的方面

package com.michaelt.so.doub;

import java.util.HashSet;
import java.util.Set;

public class CloseEnough {
    private Double d;
    protected long masked;
    protected Set<Long> similar;

    public CloseEnough(Double d) {
        this.d = d;
        long bits = Double.doubleToLongBits(d);
        similar = new HashSet<Long>();
        masked = bits & 0xFFFFFFFFFFFFFFF8L; // 111...1000
        similar.add(bits);
        similar.add(bits + 1);
        similar.add(bits - 1);
    }

    Double getD() {
        return d;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof CloseEnough)) {
            return false;
        }

        CloseEnough that = (CloseEnough) o;

        for(Long bits : this.similar) {
            if(that.similar.contains(bits)) { return true; }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (int) (masked ^ (masked >>> 32));
    }
}
使用此代码会变得更容易(并非有意使用双关语):

此代码的输出为:

false true true true true false true true true true 假的 真的 真的 真的 真的
在这种情况下,位篡改是在基于代码的相似列表中完成的。同样,数字被转换成位,但是这次数学是在比较中完成的,而不是试图处理包装器对象的相等和哈希代码。

比较位不是一个好主意。类似于另一篇文章,但涉及NaN和无穷大

import java.util.ArrayList;
import java.util.List;

public class Test {


    public static void main(String[] args) {
        // should be 1.38, but end up with 1.3800000000000001
        Double d1 = 1.37d + 0.01d;
        System.out.println("d1=" + d1);

        // won't be able to test the element for containment
        List<Double> list = new ArrayList<>();
        list.add(d1);

        System.out.println(list.contains(1.38));
        System.out.println(contains(list, 1.38d, 0.00000001d));
    }

    public static boolean contains(List<Double> list, double value, double precision) {
        for (int i = 0, sz = list.size(); i < sz; i++) {
            double d = list.get(i);
            if (d == value || Math.abs(d - value) < precision) {
                return true;
            }
        }
        return false;
    }
}
import java.util.ArrayList;
导入java.util.List;
公开课考试{
公共静态void main(字符串[]args){
//应为1.38,但最终为1.3800000001
双d1=1.37d+0.01d;
系统输出打印项次(“d1=”+d1);
//无法测试元件的安全壳
列表=新的ArrayList();
列表。添加(d1);
System.out.println(list.contains(1.38));
System.out.println(包含(列表,1.38d,0.0000000 1d));
}
公共静态布尔包含(列表、双值、双精度){
for(inti=0,sz=list.size();i
背景:浮点数有舍入问题,因此永远不要将其与“=”进行比较

这是错误的。在编写浮点代码时,您确实需要保持清醒,但在许多情况下,对程序中可能出现的错误进行推理是很简单的。如果你不能做到这一点,你至少应该得到一个经验估计,你的计算值有多错误,并考虑你看到的误差是否可以接受的小

这意味着你无法避免弄脏你的手,思考你的程序在做什么。如果你要进行近似比较,你需要了解什么差异意味着两个值确实不同,什么差异意味着两个量可能相同

//应为1.38,但最终为1.3800000001

这也是错误的。请注意,距离
1.37
最近的
double
0x1.5eb851eb851ecp+0
,距离
0.01
最近的
double
0x1.47ae147ae147bp-7
。当您添加这些时,您将得到
0x1.6147ae147ae14f6p+0
,它将四舍五入到
0x1.6147ae147ae15p+0
。最接近
1.38
double
0x1.6147ae147ae14p+0

两个稍有不同的
double
s不进行比较有几个原因。这里有两个:

  • 如果他们这样做,就会破坏及物性。(将有
    a
    b
    c
    ,这样
    a==b
    b==c
    但是
    !(a==c)
  • 如果他们这样做,精心编写的数字代码将停止工作

试图在列表中找到一个
double
的真正问题是
NaN
不会将
=
与自身进行比较。您可以尝试使用一个循环来检查
pinder==haystack[i]| pinder!=pinder&&haystack[i]!=haystack[i]

在核心Java中没有优雅的数学库解决方案(据我所知)--您要么使用epsilon小于某个最小值的适当测试来进行自己的测试,要么使用第三方库。说到这一点,我相信Apache库也有类似的东西。可能会重复@HoverCraftFullOfels,您只需编写自己的方法来检查列表是否包含值通过在集合上循环。它将同样有效,实现也很简单。气垫船提到的Apache commons方法:浮点数可以与
==
进行比较,所以说“永不”是不正确的。但你必须理解你在做什么,并记住我所做的是算术运算n产生舍入错误,而不是比较。这不是做不必要的自动装箱/取消装箱吗?@rds你如何避免它?@rds如果他有一个
双[]
,他可以使用
数组.stream()
创建一个也有
anyMatch()
。对!对不起。另外,代码需要调整,如果你想检查的话 false true true true true
package com.michaelt.so.doub;

import java.util.ArrayList;

public class SimilarList extends ArrayList<Double> {

    @Override
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < this.size(); i++) {
                if (get(i) == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < this.size(); i++) {
                if (almostEquals((Double)o, this.get(i))) {
                    return i;
                }
            }
        }
        return -1;
    }

    private boolean almostEquals(Double a, Double b) {
        long abits = Double.doubleToLongBits(a);
        long bbits = Double.doubleToLongBits(b);

        // Handle +0 == -0
        if((abits >> 63) != (bbits >> 63)) {
            return a.equals(b);
        }

        long diff = Math.abs(abits - bbits);

        if(diff <= 1) {
            return true;
        }

        return false;
    }
}
package com.michaelt.so.doub;

import java.util.ArrayList;

public class ListTest {
    public static void main(String[] args) {
        ArrayList foo = new SimilarList();

        foo.add(1.38);
        foo.add(1.40);
        foo.add(0.02);
        foo.add(0.20);

        System.out.println(foo.contains(0.0));
        System.out.println(foo.contains(1.37 + 0.01));
        System.out.println(foo.contains(1.39 + 0.01));
        System.out.println(foo.contains(0.19 + 0.01));
        System.out.println(foo.contains(0.01 + 0.01));
    }
}
false true true true true
import java.util.ArrayList;
import java.util.List;

public class Test {


    public static void main(String[] args) {
        // should be 1.38, but end up with 1.3800000000000001
        Double d1 = 1.37d + 0.01d;
        System.out.println("d1=" + d1);

        // won't be able to test the element for containment
        List<Double> list = new ArrayList<>();
        list.add(d1);

        System.out.println(list.contains(1.38));
        System.out.println(contains(list, 1.38d, 0.00000001d));
    }

    public static boolean contains(List<Double> list, double value, double precision) {
        for (int i = 0, sz = list.size(); i < sz; i++) {
            double d = list.get(i);
            if (d == value || Math.abs(d - value) < precision) {
                return true;
            }
        }
        return false;
    }
}