在Java中,如何测试'Double'列表是否包含特定值
背景:浮点数有舍入问题,因此永远不要将其与“=”进行比较 问:在Java中,如何测试在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
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;
}
}