java编程中float/double类型的正确比较方法

位置:首页>文章>详情   分类: 教程分享 > Java教程   阅读(2081)   2023-03-28 11:29:14
    实际上,这不仅是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




 
地址:https://www.leftso.com/article/373.html

相关阅读

Java编程软件有哪些?常用Java编程软件下载、安装和使用说明
java基础编程中float/double类型的正确比较方法
java8 Function函数编程详解Function函数基础定义和使用 public static void t1(){ Function&lt;Integer,Int...
Java如何复制目录,Java基础教程系列,如果要将目录及其包含的所有子文件夹和文件从一个位置复制到另一个位置,请使用下面的代码,该代码使用递归遍历目录结构,然后使用Files.copy()函数...
java编程之java jwt token使用,autho0的Java-jwt框架使用,java编程,java-jwt
Java编程之java static关键字,Java编程,static关键字
Java编程之spring boot FastDFS Java client使用,Java编程,FastDFS Java客户端
Java基础多线程之主线程等待子线程结束,Java基础编程之多线程入门学习篇。主要讲解几种方法来实现Java多线程中主线程等待子线程结束的最快方式。
在这篇快速文章中,我们将介绍使用标准框架 - JSR 380(也称为Bean Validation 2.0)来验证Java bean的基础知识。当然,在大多数应用程序中验证用户输入是超常见的需求...