Java中浮点数精度问题的解决方法

所属分类: 软件编程 / java 阅读数: 38
收藏 0 赞 0 分享

问题描述

在项目中用Java做浮点数计算时,发现对于4.015*100这样的计算,结果不是预料中的401.5,而是401.49999999999994。如此长的位数,对于显示来说很不友好。

问题原因:浮点数表示

查阅相关资料,发现原因是:计算机中的浮点数并不能完全精确表示。例如,对于一个double型的38414.4来说,计算机是这样存储它的:

转成二进制:1001011000001110.0110011001100110011001100110011001100
转成科

学计数法:1.0010110000011100110011001100110011001100110011001100×2^15

double型编码格式是这样的:

double 符号位1位 阶码11位 尾数52位

符号位:正数统一是0

阶码:15是正数,因此最高位是1,最低位减1,为10000001110

尾数:去掉最高位默认的1,为0010110000011100110011001100110011001100110011001100

组合起来,最终得到的编码是:0 10000001110 0010110000011100110011001100110011001100110011001100

从这里可以看出来,主要原因在于二进制编码使得小数部分无法完全精确表示,例如0.4 = 0.25 + 0.125 + ...,只能无限接近。所以在对浮点数做计算时会产生精度误差。

解决办法:高精度

Java中的BigDecimal可以支持任意精度的浮点数运算。在《Effective Java 》这本书中建议:float 和double 用来做科学计算或者是工程计算,而在商业计算中使用java.math.BigDecimal 。

BigDecimal有多种构造方法,如BigDecimal(double),BigDecimal(String),需要注意的是:构造参数为String类型时才能保证不丢失精度,因为double类型本身就是不完全精确的。故需要写成这样:BigDecimal("0.02")。

double类型的基本运算都能在BigDecimal中找到相对应的方法。另外,BigDecimal还可以配合NumberFormat做格式化输出。

BigDecimal在做运算的时候都会生成新的BigDecimal对象,因此相对double来说会带来更多的性能开销。

高精度实现初探

那么BigDecimal是如何做到能够表示任意精度的呢?这里只做一个初步的分析。

首先看BigInteger的实现。普通的int型是32位,因此有范围限制。BigInteger中有成员变量int[] mag,这样变长的int数组使得表示任意大小的整数成为可能。

再看BigDecimal的实现。它的官方介绍中说,任意一个BigDecimal都可以表示为unscaledValue × 10^-scale的形式。unscaledValue是一个任意大小的整数,在源代码中对应BigInteger intVal这个成员变量;scale是阶数,在源代码中对应int scale这个变量。这样就在BigInteger的基础上得到了BigDecimal的实现。

以上所述是小编给大家介绍的Java中浮点数精度问题的解决方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多