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

> 原文： [https://howtodoinjava.com/java/basics/correctly-compare-float-double/](https://howtodoinjava.com/java/basics/correctly-compare-float-double/)

正确地**比较浮点**或**比较双**不仅是 Java 特定的问题。 当今几乎所有编程语言都可以观察到它。 使用 [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) 标准格式存储浮点数和双精度数。 实际的存储和转换如何工作，不在本文的讨论范围之内。

现在，只需了解在计算和转换期间，可以在这些数字中引入*较小的舍入误差*。 这就是为什么不建议仅依赖相等运算符（`==`）**比较浮点数**的原因。

让我们学习**如何比较 Java** 中的浮点值。

```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`。 当我们比较这两种方法的结果时，它应该匹配。

```java
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");
}

```

程序输出。

```java
f1 = 1.0999999999999999
f2 = 1.1
f1 and f2 are not equal

```

查看控制台中打印的两个值。 将`f1`计算为`1.0999999999999999`。 其舍入问题恰恰是内部引起的。 因此，不建议将**浮点数用`'=='`运算符进行比较**。

## 2\. 双精度比较 -- 基于阈值的比较[推荐]

现在，当我们知道相等运算符的问题时，让我们解决它。 使用编程，我们无法更改存储或计算这些浮点数的方式。 因此，我们必须采用一种解决方案，在该解决方案中，我们同意确定两个可以容忍的值的差异，并且仍然认为数字相等。 该商定的值差被称为**阈值**或`ε`。

因此，现在要使用“基于阈值的**浮点比较**”，我们可以使用`Math.abs()`方法计算两个数字之间的差并将其与阈值进行比较。

```java
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");
}

```

程序输出：

```java
f1 = 1.0999999999999999
f2 = 1.1
f1 and f2 are equal using threshold

```

## 3\. 比较`double`与`BigDecimal`（推荐）

在[`BigDecimal`](https://docs.oracle.com/javase/10/docs/api/java/math/BigDecimal.html)类中，可以指定要使用的**舍入模式**和**精确度**。 使用精确的精度限制，可以解决舍入误差。

最好的部分是`BigDecimal`数字是[不可变的](https://howtodoinjava.com/java/basics/how-to-make-a-java-class-immutable/)，即，如果创建具有`"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`进行比较。

```java
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`类解决原始问题。

```java
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");
}

```

程序输出：

```java
f1 = 1.1
f2 = 1.1
f1 and f2 are equal using BigDecimal

```

这就是**比较 Java** 中的浮点数的全部。 在评论部分分享您的想法。

学习愉快！