章
目
录
正确比较 float或比较 double不仅仅是 Java 特有的问题。如今几乎所有编程语言中都可以观察到这一点。在计算机内存中,浮点数和双精度数使用IEEE 754标准格式存储。实际存储和转换如何工作,超出了本文的范围。
现在,只需了解在计算和转换过程中,这些数字可能会引入较小的舍入误差。这就是为什么不建议简单地依赖相等运算符 (==)来比较浮点数。
让我们学习如何在 Java 中比较浮点值。
1.比较double – 简单比较【不推荐】
首先看一下简单的比较方法,以了解使用==运算符比较double时出现了什么问题。在给定的程序中,我使用两种方法创建相同的浮点数(即1.1):
- 将0.1累加11次。
- 将0.1乘以11。
理论上,这两种操作都应该生成数字1.1。当我们比较这两种方法的结果时,它们应该匹配。
private static void simpleFloatsComparison()
{
//Method 1
double f1 = .0;
for (int i = 1; i <= 11; i++) {
f1 += .1;
}
//Method 2
double f2 = .1 * 11;
System.out.println("f1 = " + f1);
System.out.println("f2 = " + f2);
if (f1 == f2)
System.out.println("f1 and f2 are equal\n");
else
System.out.println("f1 and f2 are not equal\n");
}
程序输出:
f1 = 1.0999999999999999
f2 = 1.1
f1 and f2 are not equal
查看控制台中打印的两个值。f1
计算为1.0999999999999999
. 这正是四舍五入内部导致的问题。这就是为什么不建议使用运算符'=='
进行浮点数比较的原因。
2.比较double – 基于阈值的比较【推荐】
现在我们知道了等号运算符的问题,让我们来解决它。在编程中,我们不能改变这些浮点数的存储或计算方式。因此,我们必须采用一种解决方案,其中我们同意确定两个值之间的差异,我们可以容忍并仍然将这些数字视为相等。这些值之间的同意差异称为阈值或epsilon。
因此,为了使用“基于阈值的浮点数比较”,我们可以使用Math.abs()方法计算两个数字之间的差异,然后将差异与阈值进行比较。这样做可以确保我们在比较浮点数时考虑到了精度差异。
private static void thresholdBasedFloatsComparison()
{
final double THRESHOLD = .0001;
//Method 1
double f1 = .0;
for (int i = 1; i <= 11; i++) {
f1 += .1;
}
//Method 2
double f2 = .1 * 11;
System.out.println("f1 = " + f1);
System.out.println("f2 = " + f2);
if (Math.abs(f1 - f2) < THRESHOLD)
System.out.println("f1 and f2 are equal using threshold\n");
else
System.out.println("f1 and f2 are not equal using threshold\n");
}
程序输出:
f1 = 1.0999999999999999
f2 = 1.1
f1 and f2 are equal using threshold
3.比较double – 使用BigDecimal比较【推荐】
在BigDecimal类中,您可以指定要使用的舍入模式和精确度限制。使用精确度限制,舍入误差大多可以解决。
最重要的一点是,BigDecimal数字是不可变的,即如果您创建了一个值为“1.23”的BigDecimal对象BD,那么该对象将始终是“1.23”,永远不会更改。此类提供了许多方法,可用于对其值进行数值操作。
您可以使用其compareTo()方法来比较两个BigDecimal数字。在比较时,它忽略了标度(小数点后的位数)。
a.compareTo(b);
方法返回:
-1:如果a<b
0 :如果 a == b
1 : 如果 a > b
使用BigDecimal类的compareTo方法来比较double值:
private static void testBdEquality()
{
BigDecimal a = new BigDecimal("2.00");
BigDecimal b = new BigDecimal("2.0");
System.out.println(a.equals(b)); // false
System.out.println(a.compareTo(b) == 0); // true
}
现在,只是为了验证,让我们使用 BigDecimal 类来解决原始问题:
private static void bigDecimalComparison()
{
//Method 1
BigDecimal f1 = new BigDecimal("0.0");
BigDecimal pointOne = new BigDecimal("0.1");
for (int i = 1; i <= 11; i++) {
f1 = f1.add(pointOne);
}
//Method 2
BigDecimal f2 = new BigDecimal("0.1");
BigDecimal eleven = new BigDecimal("11");
f2 = f2.multiply(eleven);
System.out.println("f1 = " + f1);
System.out.println("f2 = " + f2);
if (f1.compareTo(f2) == 0)
System.out.println("f1 and f2 are equal using BigDecimal\n");
else
System.out.println("f1 and f2 are not equal using BigDecimal\n");
}
程序输出:
f1 = 1.1
f2 = 1.1
f1 and f2 are equal using BigDecimal
这就是java 中比较浮点数的全部内容。在评论部分分享您的想法。