注:转自 https://blog.csdn.net/bleach_kids/article/details/49129943java
在使用Java,double 进行运算时,常常出现精度丢失的问题,老是在一个正确的结果左右偏0.0000**1。 特别在实际项目中,经过一个公式校验该值是否大于0,若是大于0咱们会作一件事情,小于0咱们又处理其余事情。 这样的状况经过double计算出来的结果去和0比较大小,尤为是有小数点的时候,常常会由于精度丢失而致使程序处理流程出错。git
BigDecimal
在《Effective Java》这本书中也提到这个原则,float和double只能用来作科学计算或者是工程计算,在商业计算中咱们要用 java.math.BigDecimal。BigDecimal一共有4个够造方法,咱们不关心用BigInteger来够造的那两个,那么还有两个, 它们是:
BigDecimal(double val)
Translates a double into a BigDecimal.
BigDecimal(String val)
Translates the String repre sentation of a BigDecimal into a BigDecimal.
上面的API简要描述至关的明确,并且一般状况下,上面的那一个使用起来要方便一些。咱们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪一个够造方法的详细说明中有这么一段:
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.
原来咱们若是须要精确计算,非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子是用String来够造BigDecimal的,可是书上却没有强调这一点,这也许是一个小小的失误吧。
解决方案
如今咱们已经能够解决这个问题了,原则是使用BigDecimal而且必定要用String来够造。
可是想像一下吧,若是咱们要作一个加法运算,须要先将两个浮点数转为String,而后够形成BigDecimal,在其中一个上调用add方法,传入另 一个做为参数,而后把运算的结果(BigDecimal)再转换为浮点数。你可以忍受这么烦琐的过程吗?下面咱们提供一个工具类Arith来简化操做。它 提供如下静态方法,包括加减乘除和四舍五入:
public static double add(double v1,double v2)
public static double sub(double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)算法
因此通常对double类型进行运算时,作好对结果进行处理,而后拿这个值去作其余事情。 app
使用以下:ide
1 /** 2 * 对double数据进行取精度. 3 * @param value double数据. 4 * @param scale 精度位数(保留的小数位数). 5 * @param roundingMode 精度取值方式. 6 * @return 精度计算后的数据. 7 */ 8 public static double round(double value, int scale, 9 int roundingMode) { 10 BigDecimal bd = new BigDecimal(value); 11 bd = bd.setScale(scale, roundingMode); 12 double d = bd.doubleValue(); 13 bd = null; 14 return d; 15 } 16 17 18 /** 19 * double 相加 20 * @param d1 21 * @param d2 22 * @return 23 */ 24 public double sum(double d1,double d2){ 25 BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 26 BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 27 return bd1.add(bd2).doubleValue(); 28 } 29 30 31 /** 32 * double 相减 33 * @param d1 34 * @param d2 35 * @return 36 */ 37 public double sub(double d1,double d2){ 38 BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 39 BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 40 return bd1.subtract(bd2).doubleValue(); 41 } 42 43 /** 44 * double 乘法 45 * @param d1 46 * @param d2 47 * @return 48 */ 49 public double mul(double d1,double d2){ 50 BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 51 BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 52 return bd1.multiply(bd2).doubleValue(); 53 } 54 55 56 /** 57 * double 除法 58 * @param d1 59 * @param d2 60 * @param scale 四舍五入 小数点位数 61 * @return 62 */ 63 public double div(double d1,double d2,int scale){ 64 // 固然在此以前,你要判断分母是否为0, 65 // 为0你能够根据实际需求作相应的处理 66 67 BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 68 BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 69 return bd1.divide 70 (bd2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 71 }
这样,计算double类型的数据计算问题就能够处理了。
另外补充一下 JavaScript 四舍五入的方法:
小数点问题 工具
1 Math.round(totalAmount*100)/100 (保留 2 位) 2 3 function formatFloat(src, pos) 4 { 5 return Math.round(src*Math.pow(10, pos))/Math.pow(10, pos); 6 }
四舍五入是咱们小学的数学问题,这个问题对于咱们程序猿来讲就相似于1到10的加减乘除那么简单了。在讲解之间咱们先看以下一个经典的案例:this
1 public static void main(String[] args) { 2 System.out.println("12.5的四舍五入值:" + Math.round(12.5)); 3 System.out.println("-12.5的四舍五入值:" + Math.round(-12.5)); 4 }
Output:
12.5的四舍五入值:13
-12.5的四舍五入值:-12 spa
这是四舍五入的经典案例,也是咱们参加校招时候常常会遇到的(貌似我参加笔试的时候遇到过好屡次)。从这儿结果中咱们发现这两个绝对值相同的数字,为什么近似值会不一样呢?其实这与Math.round采用的四舍五入规则来决定。.net
四舍五入其实在金融方面运用的很是多,尤为是银行的利息。咱们都知道银行的盈利渠道主要是利息差,它从储户手里收集资金,而后放贷出去,期间产生的利息差就是银行所得到的利润。若是咱们采用日常四舍五入的规则话,这里采用每10笔存款利息计算做为模型,以下:code
四舍:0.000、0.00一、0.00二、0.00三、0.004。这些舍的都是银行赚的钱。
五入:0.00五、0.00六、0.00七、0.00八、0.009。这些入的都是银行亏的钱,分别为:0.00五、0.00四、.00三、0.00二、0.001。
因此对于银行来讲它的盈利应该是0.000 + 0.001 + 0.002 + 0.003 + 0.004 - 0.005 - 0.004 - 0.003 - 0.002 - 0.001 = -0.005。从结果中能够看出每10笔的利息银行可能就会损失0.005元,千万别小看这个数字,这对于银行来讲就是一笔很是大的损失。面对这个问题就产生了以下的银行家涉入法了。该算法是由美国银行家提出了,主要用于修正采用上面四舍五入规则而产生的偏差。以下:
舍去位的数值小于5时,直接舍去。
舍去位的数值大于5时,进位后舍去。
当舍去位的数值等于5时,若5后面还有其余非0数值,则进位后舍去,若5后面是0时,则根据5前一位数的奇偶性来判断,奇数进位,偶数舍去。
对于上面的规则咱们举例说明
11.556 = 11.56 ------六入
11.554 = 11.55 -----四舍
11.5551 = 11.56 -----五后有数进位
11.545 = 11.54 -----五后无数,若前位为偶数应舍去
11.555 = 11.56 -----五后无数,若前位为奇数应进位
下面实例是使用银行家舍入法:
1 public static void main(String[] args) { 2 BigDecimal d = new BigDecimal(100000); //存款 3 BigDecimal r = new BigDecimal(0.001875*3); //利息 4 BigDecimal i = d.multiply(r).setScale(2,RoundingMode.HALF_EVEN); //使用银行家算法 5 6 System.out.println("季利息是:"+i); 7 }
Output:
季利息是:562.50
在上面简单地介绍了银行家舍入法,目前java支持7中舍入法:
一、 ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
二、 ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,全部的位都要舍弃,不存在进位状况。
三、 ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。如果正数,舍入行为相似于ROUND_UP,若为负数,舍入行为相似于ROUND_DOWN。Math.round()方法就是使用的此模式。
四、 ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。如果正数,舍入行为相似于ROUND_DOWN;若为负数,舍入行为相似于ROUND_UP。
五、 HALF_UP:最近数字舍入(5进)。这是咱们最经典的四舍五入。
六、 HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
七、 HAIL_EVEN:银行家舍入法。
提到四舍五入那么保留位就必不可少了,在java运算中咱们可使用多种方式来实现保留位。
方法一:四舍五入
1 double f = 111231.5585; 2 BigDecimal b = new BigDecimal(f); 3 double f1 = b.setScale(2, RoundingMode.HALF_UP).doubleValue();
在这里使用BigDecimal ,而且采用setScale方法来设置精确度,同时使用RoundingMode.HALF_UP表示使用最近数字舍入法则来近似计算。在这里咱们能够看出BigDecimal和四舍五入是绝妙的搭配。
方式二:
1 java.text.DecimalFormat df =new java.text.DecimalFormat(”#.00″); 2 df.format(你要格式化的数字);
例:new java.text.DecimalFormat(”#.00″).format(3.1415926)
#.00 表示两位小数 #.0000四位小数 以此类推…
方式三:
1 double d = 3.1415926; 2 3 String result = String .format(”%.2f”);
%.2f %. 表示 小数点前任意位数 2 表示两位小数 格式后的结果为f 表示浮点型。
方式四:
此外若是使用struts标签作输出的话,有个format属性,设置为format="0.00"就是保留两位小数
例如:
1 <bean:write name="entity" property="dkhAFSumPl" format="0.00" /> 2 3 或者 4 5 <fmt:formatNumber type="number" value="${10000.22/100}" maxFractionDigits="0"/> 6 7 maxFractionDigits表示保留的位数
4:对于通常add、subtract、multiply方法的小数位格式化以下: