在 Java 中比较浮点数或双精度数的正确方法

在 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. 添加.1 11 次。
  2. .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. 比较doubleBigDecimal(推荐)

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 中的浮点数的全部。 在评论部分分享您的想法。

学习愉快!