实际上,这不仅是Java特有的问题。 几乎所有的编程语言都可以观察到它。 在计算机内存中,使用IEEE 754标准格式存储浮动和双打。 实际的存储和转换如何工作,它超出了本文的范围。
  现在,只要理解在计算和转换过程中可以在这些数字中引入较小的舍入误差。 这就是为什么仅仅依靠等式运算符(==)来比较浮点数是不可取的原因。

1.简单的比较方式[不推荐]数据不准确

首先看看简单的比较,以便理解简单比较究竟有什么错误。 在给定的程序中,我使用两种方法创建相同的浮点数(即1.1):
i)加1,11次。
i)乘以.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.基于阈值的比较[推荐]

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.使用BigDecimal比较[推荐]

在BigDecimal类中,您可以指定要使用的舍入模式和精确精度。 使用精确的精度限制,舍入误差大多可以解决。

最好的部分是BigDecimal数字是不可变的,即如果您创建一个值为“1.23”的BigDecimal BD,该对象将保持为“1.23”并且永远不会被更改。 这个类提供了很多方法可以用来对它的值进行数字操作。

您可以使用它的.compareTo()方法与BigDecimal数字进行比较。 比较时忽略比例。
a.compareTo(b);  // returns (-1 if a < b),  (0 if a == b),  (1 if a > b)

切勿使用.equals()方法比较BigDecimals。 那是因为这个等于函数会比较尺度。 如果比例不同,.equals()将返回false,即使它们是数学上相同的数字。


我们举一个例子来理解这个比较。
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