JAVA浮点数计算精度损失底层原理与解决方案

 更新时间:2017年02月14日 09:52:43   作者:星火燎原智勇  
本文主要介绍了JAVA浮点数计算精度损失底层原理与解决方案。具有很好的参考价值,下面跟着小编一起来看下吧

问题:

对两个double类型的值进行运算,有时会出现结果值异常的问题。比如:

  System.out.println(19.99+20);
  System.out.println(1.0-0.66);
  System.out.println(0.033*100);
  System.out.println(12.3/100);

输出:

39.989999999999995
0.33999999999999997
3.3000000000000003
0.12300000000000001

Java中的简单浮点数类型float和double不能够精确运算。这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。

关于精度损失的原理可以很简单的讲,首先一个正整数在计算机中表示使用01010形式表示的,浮点数也不例外。

比如11,11除以2等于5余1

       5除以2等于2余1

       2除以2等于1余0

       1除以2等于0余1

所以11二进制表示为:1011.

double类型占8个字节,64位,第1位为符号位,后面11位是指数部分,剩余部分是有效数字。

正整数除以2肯定会有个尽头的,之后二进制还原成十进制只需要乘以2即可。

举个例子:0.99用的有效数字部分,

       0.99 * 2 = 1+0.98 --> 1
       0.98 * 2 = 1+0.96 --> 1
       0.96 * 2 = 1+0.92 -- >1
       0.92 * 2 = 1+0.84 -- >1
         ...............

这样周而复始是没法有尽头的,而double有效数字有限,所以必定会有损失,所以二进制无法准确表示0.99,就像十进制无法准确表示1/3一样。

解决办法:

在《Effective Java》中提到一个原则,那就是float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal,通过使用BigDecimal类可以解决上述问题,首先需要注意的是,直接使用字符串来构造BigDecimal是绝对没有精度损失的,如果用double或者把double转化成string来构造BigDecimal依然会有精度损失,所以我觉得这种解决方法就是在使用中就把浮点数用string来表示存放,涉及到运算直接用string构造double,否则肯定会有精度损失。

1. 相加

/**
 * 相加
 * @param double1
 * @param double2
 * @return
 */
public static double add(String doubleValA, String doubleValB) { 
  BigDecimal a2 = new BigDecimal(doubleValA); 
  BigDecimal b2 = new BigDecimal(doubleValB); 
  return a2.add(b2).doubleValue(); 
}

2. 相减

/**
 * 相减
 * @param double1
 * @param double2
 * @return
 */
public static double sub(String doubleValA, String doubleValB) { 
  BigDecimal a2 = new BigDecimal(doubleValA); 
  BigDecimal b2 = new BigDecimal(doubleValB); 
  return a2.subtract(b2).doubleValue();
}

3. 相乘

/**
 * 相乘
 * @param double1
 * @param double2
 * @return
 */
public static double mul(String doubleValA, String doubleValB) { 
  BigDecimal a2 = new BigDecimal(doubleValA); 
  BigDecimal b2 = new BigDecimal(doubleValB); 
  return a2.multiply(b2).doubleValue();
}

4. 相除

/**
 * 相除
 * @param double1
 * @param double2
 * @param scale 除不尽时指定精度
 * @return
 */
public static double div(String doubleValA, String doubleValB, int scale) { 
  BigDecimal a2 = new BigDecimal(doubleValA); 
  BigDecimal b2 = new BigDecimal(doubleValB);
  return a2.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 
}

5. 主函数调用

public static void main(String[] args) {
  String doubleValA = "3.14159267";
  String doubleValB = "2.358";
  System.out.println("add:" + add(doubleValA, doubleValB));
  System.out.println("sub:" + sub(doubleValA, doubleValB));
  System.out.println("mul:" + mul(doubleValA, doubleValB));
  System.out.println("div:" + div(doubleValA, doubleValB, 8));
}

结果展示如下所示:

 add:5.49959267
 sub:0.78359267
 mul:7.40787551586
 div:1.33231241

所以最好的方法是完全抛弃double,用string和java.math.BigDecimal。

java遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。具体底层如何存储以及如何进行运行请继续关注我的博客,后续我会将详情总结好的。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

相关文章

  • Java稀疏数组详细图文教程

    Java稀疏数组详细图文教程

    当一个数组中的大部分元素为相同的值,可使用稀疏数组来保存该数组,可以将稀疏数组看做是普通数组的压缩,这篇文章主要给大家介绍了关于Java稀疏数组的相关资料,需要的朋友可以参考下
    2023-09-09
  • IDEA如何在当前类中查找方法快捷键

    IDEA如何在当前类中查找方法快捷键

    这篇文章主要介绍了IDEA如何在当前类中查找方法快捷键问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 详解如何使用Java8 Steam流对Map进行排序

    详解如何使用Java8 Steam流对Map进行排序

    这篇文章主要给大家详细介绍了如何使用Java8 Steam流对Map进行排序,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-01-01
  • spring快速入门实例教程

    spring快速入门实例教程

    这篇文章主要介绍了spring快速入门实例,主要分析了spring的基本配置与控制反转,对于spring的学习具有一定的参考借鉴价值,需要的朋友可以参考下
    2014-12-12
  • 将bean注入到Spring中的方式总结

    将bean注入到Spring中的方式总结

    在Java的Spring框架中,将bean注入到容器中是核心概念之一,这是实现依赖注入的基础,Spring提供了多种方式来将bean注入到容器中,本文给大家总结了将bean注入到Spring中的几种方式,需要的朋友可以参考下
    2023-12-12
  • Java如何把文件夹打成压缩包并导出

    Java如何把文件夹打成压缩包并导出

    这篇文章主要介绍了Java如何把文件夹打成压缩包并导出,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • javaweb实现简易邮件发送

    javaweb实现简易邮件发送

    这篇文章主要为大家详细介绍了javaweb实现简易邮件发送,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Java如何将字符串String转换为整型Int

    Java如何将字符串String转换为整型Int

    这篇文章主要介绍了Java如何将字符串String转换为整型Int,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-08-08
  • 解决IDEA插件市场Plugins无法加载的问题

    解决IDEA插件市场Plugins无法加载的问题

    这篇文章主要介绍了解决IDEA插件市场Plugins无法加载的问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • SpringCloud hystrix服务降级概念介绍

    SpringCloud hystrix服务降级概念介绍

    什么是服务降级?当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作
    2022-09-09

最新评论