BigDecimal使用2--保留小数点位数
转载自逸学堂BigDecimal 的那些坑事儿
最近查看rebate数据时,发现一个bug,主要现象是,当扣款支付宝的账号款项时,返回的是数字的金额为元,而数据库把金额存储为分,这中间要做元与分的转化,这个转化规则很简单,就是*100的,所以一开始代码很简单,如下。
Float f = Float.valueOf(s);
f =f*100;
Long result = f.longValue();
当s=”9.86”时,杯具出现了,result的结果为985而不是986,float的精度损失导致float(985.99994)转化为整形时,丢掉小数部分成为985,简单的方法,我们可以提高精度使用双精度的double类型,提高精度,比如
Double d = Double.valueOf(s);
d = d*100;
Long result = d.longValue();
当s=”9.86”时,确实能够得到正确结果,但是当s=”1219.86”时,这时候由于精度问题导致最终的result为121985为不是121986。当时以为使用double解决的问题,其实隐藏更隐蔽的bug。
Double d = Double.valueOf(s);
d = d*100+0.5;// 注意这里,我们使用的是+0.5的形式。
Long result = d.longValue();
但是,我们使用的java语言,java应该有更优雅的解决方案,那就是BigDecimal。
使用BigDecimal的解决方案成这个样子
Double dd= Double.valueOf(s);
BigDecimal bigD = new BigDecimal(dd);
bigD = bigD.multiply(new BigDecimal(100));
Long result = bigD.longValue();
狂晕,输出结果是985为不是986,打印bigD
System.out.println(bigD.toString());
输出:985.9999999999999431565811391919851303100585937500
不会再加上一个BigDecimal(0.5)吧。我相信在使用过BigDecimal过程中,肯定有那里不对的地方,multiply方法中可以传入精度,那就构造MathContext对象,修改如下。
Double dd= Double.valueOf(s);
BigDecimal bigD = new BigDecimal(dd);
MathContextmc = new MathContext(4,RoundingMode.HALF_UP);
//4表示取四位有效数字,RoundingMode.HALF_UP表示四舍五入
bigD= bigD.multiply(new BigDecimal(100),mc);
Long result = bigD.longValue();
最后结果输出为986,貌似已经找到完成解决方案,其实不然,注意到MathContext中的4了嘛?这是因为我们保留4位有效数字,假如我们输入的数字是大于4的,比如1219.86,最终输出结果是122000,这是因为1219.86保留4位有效数字时,第四位的9四舍五入,除去精确位补零,所以最终结果成了122000。问题就成了,我们必须知道元变分后的最终有效位数,”9.86”,有效位数是4,”19.86”有效位数是5,把字符串s的长度传过去就可以了,那么代码如下
Double dd =Double.valueOf(s);
BigDecimalbigD = new BigDecimal(dd);
MathContextmc = new MathContext(s.length(),RoundingMode.HALF_UP);
//4表示取四位有效数字,RoundingMode.HALF_UP表示四舍五入
bigD= bigD.multiply(new BigDecimal(100),mc);
Long result = bigD.longValue();
1 | public BigDecimal(double val) | 将double表示形式转换为BigDecimal |
---|---|---|
2 | public BigDecimal(int val) | 将int表示形式转换为BigDecimal |
3 | public BigDecimal(String val) | 将字符串表示形式转换为BigDecimal |
通过这三个构造函数,可以把double类型,int类型,String类型构造为BigDecimal对象,在BigDecimal对象内通过BigIntegerintVal存储传递对象数字部分,通过int scale;记录小数点位数,通过int precision;记录有效位数(默认为0)。
BigDecimal的加减乘除就成了BigInteger与BigInteger之间的加减乘除,浮点数的计算也转化为整形的计算,可以大大提供性能,并且通过BigInteger可以保存大数字,从而实现真正大十进制的计算,在整个计算过程中,还涉及scale的判断和precision判断从而确定最终输出结果。
我们先看一个例子
BigDecimal d1 = new BigDecimal(0.6);
BigDecimal d2 = new BigDecimal(0.4);
BigDecimal d3 = d1.divide(d2);
System.out.println(d3);
大家猜一下,以上输出结果是?再接着看下面的代码
BigDecimal d1 = new BigDecimal(“0.6”);
BigDecimal d2 = new BigDecimal(“0.4”);
BigDecimal d3 = d1.divide(d2);
System.out.println(d3);
看似相似的代码,其结果完全不同,第一个例子中,抛出异常。第二个例子中,输出打印结果为1.5。造成这种差异的主要原因是第一个例子中的创建BigDecimal时,0.6和0.4是浮动类型的,浮点型放入BigDecimal内,其存储值为
0.59999999999999997779553950749686919152736663818359375
0.40000000000000002220446049250313080847263336181640625
这两个浮点数相除时,由于除不尽,而又没有设置精度和保留小数点位数,导致抛出异常。而第二个例子中0.6和0.4是字符串类型,由于BigDecimal存储特性,通过BigInteger记录BigDecimal的值,所以,0.6和0.4可以非常正确的记录为
0.6
0.4
两者相除得出1.5来。
对于第一个例子,如果我们想得到正确结果,可以这样来
BigDecimal d1 = new BigDecimal(0.6);
BigDecimal d2 = new BigDecimal(0.4);
BigDecimal d3 = d1.divide(d2, 1, BigDecimal.ROUND_HALF_UP);
现在看我们留下的那个问题,使用更优雅的方式解决元转化为分的方式,上一个问题中,我们通过传递s.length()从而获得精度,如果之前的s是double类型的,那边这样的方式就会有问题,通过上面的例子,我们可以调整为一下的通用方式
Double dd= Double.valueOf(s);
BigDecimal bigD = new BigDecimal(dd);
bigD = bigD.multiply(newBigDecimal(100)). divide(1, 1, BigDecimal.ROUND_HALF_UP);
Long result = bigD.longValue();
我们通过/1,然后设置保留小数点方式,以及设置数字保留模式,从而得到两个数乘积的小数部分。还有以下模式
枚举常量摘要
ROUND_CEILING 向正无限大方向舍入的舍入模式。
ROUND_DOWN 向零方向舍入的舍入模式。
ROUND_FLOOR 向负无限大方向舍入的舍入模式。
ROUND_HALF_DOWN 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。
ROUND_HALF_EVEN 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
ROUND_HALF_UP 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。
ROUND_UNNECESSARY 用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。(默认模式)
ROUND_UP 远离零方向舍入的舍入模式。
总结:
1:尽量避免传递double类型,有可能话,尽量使用int和String类型。
2:做乘除计算时,一定要设置精度和保留小数点位数。
3:BigDecimal计算时,单独放到try catch内。
BigDecimal使用2--保留小数点位数相关推荐
- 少儿编程100讲轻松学python(十一)-python如何保留小数点位数
前言 python保留小数点位数的方法:首先新建py文件,输入[a=('%.2f'%a)]即可保留2位小数:然后如果输入[a=('%.4f'%a)],就保留4位小数:最后也可以输入[a=format( ...
- Python:使用f-string保留小数点位数
Python:使用f-string保留小数点位数 格式 f"{num:xxx}" 其中xxx的格式如下 格式 说明 width 整数width指定宽度 0width 整数width ...
- matlab读取excel,求导、函数、注释,保留小数点位数等
0.安装 2016a 破解版教程: https://jingyan.baidu.com/article/72ee561a19688be16138df3b.html 1.matlab大段注释的方法: 注 ...
- C++知识精讲5——printf()函数保留小数点位数方法及实战运用基本方式
本文我们来讲C++知识精讲的第5篇,printf函数以及实战运用,此专栏会讲许多,各种各样的类型,如果喜欢此专栏请订阅持续关注,感谢大家的支持.接下来,进入今天的知识精讲. printf用来干什么的? ...
- c++ 四舍五入保留两位小数_Excel中保留小数点位数
今天老板让我把带有小数点的表格进行整理,老板说把数据整理成保留两位,默认为四舍五入,老板还要整理成一种不四舍五入的数据.好的老板马上整理,这次老板的要求正好是我会的,真棒!我们一起来学一下吧. 首先我 ...
- mysql小数点后保留两位_不会保留小数点位数,做出来的表格难看,巧用ROUND函数解决...
先提问一下,在Excel中,利用公式计算时,计算出来的结果有时候会有N多位小数,很多同学是不是就直接选择单元格格式设置"数值"保留两位小数点,就OK了.然鹅,有没有细心的同学发现, ...
- Java四舍五入及保留小数点位数
之前有看过网上有的方法,比如可以利用字符串截取,还有就是运用DecimalFormat类来完成 /*** 格式化double<br>* 对 {@link DecimalFormat} 做封 ...
- android设置大小能用小数,Android中关于保留小数点位数的处理
保留两位小数 方法一: { double c = 3.154215; java.text.DecimalFormat myformat=new java.text.DecimalFormat(&quo ...
- 保留小数点位数和格式
JS 中 1. double运算 c = parseFloat(a) + parseFloat(b)) 2. 四舍五入,保留两位小数 c = c.toFixed(2); 3. 去掉小数点后面多余的0 ...
最新文章
- 团队如何实施敏捷开发以及Scrum电子看板工具
- Deepfake让罗伯特·德尼罗用流利的德语表演台词!差点忘了他是美国人
- 界面之下:还原真实的MV*模式
- 【深度学习】新的深度学习优化器探索(协同优化)
- 信息传递服务器,AJAX的与服务器之间的信息传递原理(初学)
- 旧android 4 平板,如今的安卓平板值不值得买:小米平板4入坑指南
- 笔试编程题常用的一些技巧方法
- MacOS实现MSDOS格式化为NTFS文件系统
- php加速 PHP APC 浅析
- php unset引用变量后不会删除值
- 驱动总裁 ,解决屏幕亮度无法调节(含泪推荐,屏幕亮度终于可以调整了,发热也不严重了)
- 淘淘商城系列——Solr集群搭建
- 【JVM】深入理解JVM垃圾回收机制及其垃圾回收算法
- 支付宝转账提现相关问题
- html如何实现自动登录,Js实现下次自动登录功能
- 实验2 格式化输入输出和分支语句
- 编程15年,如何才能成不了高手?
- 自定义数据集(Pokemon)实战
- 解决win10下PPT打不开,显示内容有问题,提示修复但修复不成功问题
- matlab模糊度函数,模糊函数 matlab 模糊度