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

问题描述

在项目中用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中浮点数精度问题的解决方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言。

(0)

相关推荐

  • 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中的简

  • java实现随机输出300题四则运算

    话不多说,请看代码: package demo; import java.util.Random; public class math { public static void main(String[] args) { String[] operate=new String[]{"+","-","×","÷"}; int[] numbers=new int[1000]; for(int i=1;i<=1000;i++)

  • Java中精确的浮点运算操作示例

    本文实例讲述了Java中精确的浮点运算操作.分享给大家供大家参考,具体如下: Java中浮点运算对于很多值浮点数都是采用其能够表示的离目标值最近的数来表示,这有可能会在计算中带来不易觉察的误差. 如下所例: public class ssss { public static void main(String[] ages){ double d1=2.07; double d2=1.03; System.out.println(d1+d2); } } 结果: 虽然计算结果离精确值误差很小,但其不是

  • Java常用数字工具类 大数乘法、加法、减法运算(2)

    上篇分享了一下数字转汉字的小功能,这里再分享一下大数相乘.相加.相减的功能.其他的不做过多的铺垫了,我先讲一下各个功能的计算原理. Ⅰ. 乘法运算 为什么先说乘法运算--因为我先做了乘法运算.其实思路也是很多的,但是最终我参考了网络上的一种计算方案,然后做了很多的修改.感觉这个在思路上应该是比较简单的. 简单点说:把数拆分成整数小数分别进行乘法运算,然后将结果放入一个特定长度的数组中,在放入是要计算存放的偏移位置,最后再对这个进行处理(进位.标记等),得到最终的结果. 是不是有点晕.请我详细说一

  • Java中的浮点数分析

    文章来源:csdn 作者:treeroot 浮点数分为单精度和双精度,Java中的单精度和双精度分别为float和double.你们知道float和double是怎么存储的吗? float占4个字节,double占8个字节,为了方便起见,这里就只讨论float类型. float其实和一个int型的大小是一样的,一共32位,第一位表示符号,2-9表示指数,后面23位表示小数部分.这里不多说,请参考:http://blog.csdn.net/treeroot/archive/2004/09/05/9

  • java大数乘法的简单实现 浮点数乘法运算

    复制代码 代码如下: import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.regex.Matcher;import java.util.regex.Pattern; /** * 大数乘法的简单实现, 目前还不是很完善 * Fix:  * 1. 修改前后删除0的一些错误情况 * 2. 支持负数运算 * 3. 判断输入字符串是否符合小数定义

  • java实现浮点数转人民币的小例子

    复制代码 代码如下: import java.util.ArrayList; import java.util.List; public class RMBConverter2 implements IRMBConverter { private static final String [] RMB_NUMBER ={"零","壹","贰","叁","肆","伍","陆&quo

  • Java中使用BigDecimal进行精确运算

    首先我们先来看如下代码示例: public class Test_1 { public static void main(String[] args) { System.out.println(0.06+0.01); System.out.println(1.0-0.42); System.out.println(4.015*100); System.out.println(303.1/1000); } } 运行结果如下. 0.06999999999999999 0.58000000000000

  • java中double类型运算结果异常的解决方法

    问题: 对两个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 解决方法: J

  • Java中使用BigDecimal进行浮点数运算

    最近研究了一下Java的浮点数计算问题,从网上查询了相关的资料,汇总并经过了一些整理和调试,最后完成此文,欢迎大家指出其中的错误和问题. 在Java中,float声明的变量是单精度浮点数,double声明的变量是双精度浮点数,顾名思义就是double型的实体占用内存空间是float的两倍.float是4个字节而double是8个字节.float和double类型的数据,无法精确表示计算结果,这是由于float和double是不精确的计算.大家可以通过下面代码可以看出来: 复制代码 代码如下: p

  • 【Java】BigDecimal实现加减乘除运算代码

    java.math.BigDecimal BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val) Translates a double into a BigDecimal. 第二种:BigDecimal(String val) Translates the String repre sentation of a BigDecimal into a BigDecimal. 使用BigDecimal要用String来够造,要做一个

随机推荐