在 Java 中比较浮点数或双精度数的正确方法
原文: https://howtodoinjava.com/java/basics/correctly-compare-float-double/
正确地比较浮点或比较双不仅是 Java 特定的问题。 当今几乎所有编程语言都可以观察到它。 使用 IEEE 754 标准格式存储浮点数和双精度数。 实际的存储和转换如何工作,不在本文的讨论范围之内。
现在,只需了解在计算和转换期间,可以在这些数字中引入较小的舍入误差。 这就是为什么不建议仅依赖相等运算符(==
)比较浮点数的原因。
让我们学习如何比较 Java 中的浮点值。
Table of Contents
1\. Simple comparison [Not recommended]
2\. Threshold based comparison [Recommended]
3\. Compare with BigDecimal [Recommended]
1. 双精度比较 – 简单比较(不推荐)
首先看一下简单比较,以了解用==
运算符比较double
到底有什么问题。 在给定的程序中,我使用两种方法创建相同的浮点数(即1.1
):
- 添加
.1
11 次。 - 将
.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. 双精度比较 – 基于阈值的比较[推荐]
现在,当我们知道相等运算符的问题时,让我们解决它。 使用编程,我们无法更改存储或计算这些浮点数的方式。 因此,我们必须采用一种解决方案,在该解决方案中,我们同意确定两个可以容忍的值的差异,并且仍然认为数字相等。 该商定的值差被称为阈值或ε
。
因此,现在要使用“基于阈值的浮点比较”,我们可以使用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
,则该对象将保持"1.23"
且不能更改。 此类提供了许多方法,可用于对其值进行数值运算。
您可以使用compareTo()
方法与BigDecimal
数字进行比较。 比较时会忽略比例。
a.compareTo(b);
方法返回:
-1 – 如果是
a < b
0 – 如果
a == b
1 – 如果是
a > b
切勿使用equals()
方法比较BigDecimal
实例。这是因为此equals
函数将比较比例。 如果比例不同,即使在数学上相同,equals()
也将返回false
。
Java 程序将BigDecimal
类与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 中的浮点数的全部。 在评论部分分享您的想法。
学习愉快!