有趣的Java之调皮的浮点数
**当你在写一个电商网站的时候,你可能会给你的商品标价1.99,10.9这样的价格来吸引顾客.我应该用浮点数float/double来储存它们,当我的顾客购买商品的时候,从他们的账户里扣费,使用整型是不可能的.你可能会这么想.
**如果你的顾客账号里的钱刚好是1950.00,5001.00这样的整元,那么恭喜你,你的想法是没有错的.但是一旦有一位顾客的账号里还有若干毛,若干分钱没花完,那你的程序就会得到一个错误的数值!很无奈的是,你无法通过报错信息找到它,如果你已经用浮点型float/double来计算,那么你将很难发现这个错误!
**例如,有这样一个业务需要处理:顾客有500.05块钱,你的商品定价19.9元.
double d1 = 500.05;
double d2 = 19.9;
double d3 = d1 - d2;
**你可能想顾客钱包里应该还有480.15元.但是你得到的结果是480.15000000000003元.虽然相差很少,但是日积月累或者当你在经营一个亿万数据的网站,这将是个极大的BUG!
那么我们该怎么解决这个问题呢?
**如果你要解决某个问题,那么你首先要知道问题出现在哪.这是我写程序的准则那么问题出现在哪呢?经过百度+摸索,发现十进制数的二进制表示可能不够精确.浮点数值没办法用十进制来精确表示的原因要归咎于CPU表示浮点数的方法。这样的话您就可能会牺牲一些精度,有些浮点数运算也会引入误差。以上面提到的情况为例,480.15的二进制表示并非就是精确的480.15。反而最为接近的二进制表示是 480.15000000000003。原因在于浮点数由两部分组成:指数和尾数。
Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。 这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。 在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。这个问题有两种方案.
1.你将得到的数经过放大n个整数倍后再除以n个整数倍.它的原理和java进制转换的原理相同.
2.不要用浮点数double/float进行计算,选择BigDecimal进行精确运算.
在使用BigDecimal类来进行计算的时候,主要分为以下步骤:
a、用float或者double变量构建BigDecimal对象。
b、通过调用BigDecimal的加,减,乘,除等相应的方法进行算术运算。
c、把BigDecimal对象转换成float,double,int等类型。
BigDecimal能够很好地对浮点数进行运算.但是他的计算过程相对来说十分麻烦,而且因为每次计算都要创建一个实例,十分消耗资源.以下举例说明它的计算过程
—->>>>
BigDecimal b1 = new BigDecimal(Double.toString(500.05));
BigDecimal b2 = new BigDecimal(Double.toString(19.9));
System.out.println(b1.subtract(b2));
****要注意的是,如果你直接在BigDecimal b1 = new BigDecimal(Double.toString(500.05));里面写浮点数,依旧会出现精确度的偏差!
****最后,感谢同学魏勇/黎裕锦和我分享这个问题,如果后续有更加优秀的解决方案,我也会及时贴上来!**
有趣的Java之调皮的浮点数的更多相关文章
- java中float/double浮点数的计算失精度问题(转)
如果我们编译运行下面这个程序会看到什么? public class Test { public static void main(String args[]) { ...
- Java比较两个浮点数
浮点数的基本数据类型不能用==比较,包装数据类型不能用 equals 比较 浮点数的表示 在计算机系统中,浮点数采用 符号+阶码+尾数 进行表示.在Java中,单精度浮点数float类型占32位,它的 ...
- 怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
这么说吧,在我眼里,Java 就是最流行的编程语言,没有之一(PHP 往一边站).不仅岗位多,容易找到工作,关键是薪资水平也到位,不学 Java 亏得慌,对吧? 那可能零基础学编程的小伙伴就会头疼了, ...
- Java如何正确比较浮点数
看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么? double d1 = .1 * 3; double d2 = .3; System.out.println(d1 == ...
- 很有趣的Java分形绘制
部分与整体以某种形式相似的形,称为分形. 首先我们举个例子: 我们可以看到西兰花一小簇是整个花簇的一个分支,而在不同尺度下它们具有自相似的外形.换句话说,较小的分支通过放大适当的比例后可 ...
- 记一个有趣的Java OOM!
原文:https://my.oschina.net/u/1462914/blog/1630086 引言 熟悉Java的童鞋,应该对OOM比较熟悉.该类问题,一般都比较棘手.因为造成此类问题的原因有很多 ...
- 有趣的java小项目------猜拳游戏
package com.aaa; //总结:猜拳游戏主要掌握3个方面:1.人出的动作是从键盘输入的(System.in)2.电脑是随机出的(Random随机数)3.双方都要出(条件判断) import ...
- Java正则表达式-匹配正负浮点数
记录缘由: 公司项目需要从xml中获取标识为NUMBER的字符串,将之存入数据库中,存入的列的类型即为NUMBER.当遇到非数字时,原实现是通过异常: String plainValue = null ...
- 浮点数转换为人名币读法字符串(JAVA)
/*<java疯狂讲义>浮点数转换为人名币读法字符串这个用例,感觉没有考虑零的情况*/ import java.util.Arrays; public class Num2Rmb { pr ...
随机推荐
- catalina.out日志切割
安装cronlog rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm yum ins ...
- Centos7下安装破解confluence6.3
confluence是一个专业的企业知识管理与协同软件,可以用于构建企业wiki.通过它可以实现团队成员之间的协作和知识共享.现在大多数公司都会部署一套confluence与jira的结合,用作内部w ...
- Java工具类-转换字符编码
package common; /** *字符串处理公用类 */ public class DealString { /** * 转换字符编码 由“iso-8859-1”西文转换为简体中文 */ pu ...
- mysql查询优化以及面试小结
mysql面试小结: 1.mysql的基本架构 2.mysql的索引 btree+的原理 3.mysql的索引优化 4.mysql的sql查询优化 慢查询日志 Show prodile 全局查询日志 ...
- ABP+NetCore+Vue.js实现增删改查
ABP我就不多介绍了,不知道的可以自己百度 本篇开发工具VS2017,数据库SQL SERVER2012,系统Win7 1.去ABP官网下载对应的模板,下载地址:https://aspnetboile ...
- apache配置httpd.conf相关
1.apache开启压缩AddOutputFilterByType 找到并打开apache/conf目录中的httpd.conf文件 在httpd.conf中打开deflate_Module,head ...
- [P2698][USACO12MAR]花盆Flowerpot
Link: P2698 传送门 Solution: 对于可行区间$[L,R]$,随着$L$的递增$R$不会递减 因此可以使用尺取法来解决此题:不断向右移动左右指针,复杂度保持线性 同时为了维护区间内的 ...
- BZOJ 2818 Gcd(莫比乌斯反演)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2818 [题目大意] 给定整数N,求1<=x,y<=N且Gcd(x,y)为素 ...
- 【数论】【中国剩余定理】【LCM】hdu1788 Chinese remainder theorem again
根据题目容易得到N%Mi=Mi-a. 那么可得N%Mi+a=Mi. 两侧同时对Mi取余,可得(N+a)%Mi=0. 将N+a看成一个变量,就可以把原问题转化成求Mi的LCM,最后减去a即可. #inc ...
- JDK源码学习笔记——Object
一.源码解析 public class Object { /** * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 */ private static native void ...