Java中double类型精度丢失问题
2016-05-10 23:11
661 查看
首先,之所以double浮点数类型中会出现精度不准确这个问题,是因为浮点数在计算机的存储方法导致的,要想具体了解的话,可以查看《计算机组成原理》这本书,里面有比较详尽的解答。
接下来由一道题来引出接下来要讲的这个问题:
对任意给定的一个double型的数(小数点位数大于2),要求最后输出的数,精确到小数点后两位,采用四舍五入计算。
最开始的时候我是想到一个方法,是把整数部分和小数部分分开,在小数部分直接计算四舍五入,最后输出的时候再重新加起来就好。下面是我当时写的代码:
这里面首先我没有考虑到整型溢出的问题,因为一大于Integer.MAX_VALUE或者小于Integer.MIN_VALUE,就会发生溢出。果然一个大数进去就出错了(175464815.2154354)。再者,我发现里面还有另外的错误,就是比如用2.675这个数的话,在到算小数部分的时候,x本应该是0.675的,但是最后却是变成了0.6749999999999998了,但是我也是Debug才发现的这个问题。之后上网查了之后才发现,原来是double类型精度丢失造成的。
发现之后,我就用了BigDecimal来改,但是在这里面又发现了一个问题,就是BigDecimal的构造函数中用Double的话,也是有可能会发生精度丢失的问题的。
最后,我还是跟API说的一样,将double转换为String之后在构造BigDecimal函数,这样就应该没错了吧。(由于错了太多次,不太敢相信这里边还有没有其他的什么精度丢失的情况。)
附:
其实,还有其他的方法来解决保留小数位数的,下面介绍几种:
1、
2、
3、(在Javabean中)
4、(其中,digits 显示的数字位数
为格式化对象设定小数点后的显示的最多位,显示的最后位是舍入的)
接下来由一道题来引出接下来要讲的这个问题:
对任意给定的一个double型的数(小数点位数大于2),要求最后输出的数,精确到小数点后两位,采用四舍五入计算。
最开始的时候我是想到一个方法,是把整数部分和小数部分分开,在小数部分直接计算四舍五入,最后输出的时候再重新加起来就好。下面是我当时写的代码:
public static double TwoDecimalPlaces(double number){ int z = (int)number; double x = number - z; if((int)(x * 1000) % 10 >= 5){ return z + (double)((int)(x * 100) + 1)) / 100; } return z + (double)((int)(x * 100))) / 100; }
这里面首先我没有考虑到整型溢出的问题,因为一大于Integer.MAX_VALUE或者小于Integer.MIN_VALUE,就会发生溢出。果然一个大数进去就出错了(175464815.2154354)。再者,我发现里面还有另外的错误,就是比如用2.675这个数的话,在到算小数部分的时候,x本应该是0.675的,但是最后却是变成了0.6749999999999998了,但是我也是Debug才发现的这个问题。之后上网查了之后才发现,原来是double类型精度丢失造成的。
发现之后,我就用了BigDecimal来改,但是在这里面又发现了一个问题,就是BigDecimal的构造函数中用Double的话,也是有可能会发生精度丢失的问题的。
public static double TwoDecimalPlaces(double number) { BigDecimal b = new BigDecimal(number); return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue(); }
上面代码很多测试用例都是能通过的,就是在只有三个小数位的数上精确出错,比如还是这个数2.675,本来精确之后,答案应该是2.68的,但是我上面的代码运行之后答案却是2.67。这就让我很郁闷了,到底是hi错在哪里呢?我在网上找了好久,最后还是在API中找到了答案。 API中是这样解释的: 注: 1、此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。 2、另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。 3、当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。
public static double TwoDecimalPlaces(double number) { BigDecimal b = new BigDecimal(Double.toString(number)); return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue(); }
最后,我还是跟API说的一样,将double转换为String之后在构造BigDecimal函数,这样就应该没错了吧。(由于错了太多次,不太敢相信这里边还有没有其他的什么精度丢失的情况。)
附:
其实,还有其他的方法来解决保留小数位数的,下面介绍几种:
1、
java.text.DecimalFormat df = new java.text.DecimalFormat(”#.00″); df.format(你要格式化的数字);
2、
double d = 3.1415926; String result = String .format(”%.2f”);
3、(在Javabean中)
<bean:write name="entity" property="dkhAFSumPl" format="0.00" />
4、(其中,digits 显示的数字位数
为格式化对象设定小数点后的显示的最多位,显示的最后位是舍入的)
double x=23.5455; NumberFormat ddf1=NumberFormat.getNumberInstance() ; ddf1.setMaximumFractionDigits(2); String s= ddf1.format(x) ; System.out.print(s);
目前了解的就这几种方法,要是再想到新的解法会再来更新此文章。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树