java中Double类型运算精度丢失问题小数点多出99999999999999x.docx

上传人:b****6 文档编号:5576167 上传时间:2022-12-28 格式:DOCX 页数:10 大小:19.80KB
下载 相关 举报
java中Double类型运算精度丢失问题小数点多出99999999999999x.docx_第1页
第1页 / 共10页
java中Double类型运算精度丢失问题小数点多出99999999999999x.docx_第2页
第2页 / 共10页
java中Double类型运算精度丢失问题小数点多出99999999999999x.docx_第3页
第3页 / 共10页
java中Double类型运算精度丢失问题小数点多出99999999999999x.docx_第4页
第4页 / 共10页
java中Double类型运算精度丢失问题小数点多出99999999999999x.docx_第5页
第5页 / 共10页
点击查看更多>>
下载资源
资源描述

java中Double类型运算精度丢失问题小数点多出99999999999999x.docx

《java中Double类型运算精度丢失问题小数点多出99999999999999x.docx》由会员分享,可在线阅读,更多相关《java中Double类型运算精度丢失问题小数点多出99999999999999x.docx(10页珍藏版)》请在冰豆网上搜索。

java中Double类型运算精度丢失问题小数点多出99999999999999x.docx

java中Double类型运算精度丢失问题小数点多出99999999999999x

java中Double类型运算精度丢失问题,(小数点多出99999999999999)x

Java,double进行运算时,经常出现精度丢失的问题,总是在一个正确的结果左右偏0.0000**1。

特别在实际项目中,通过一个公式校验该值是否大于0,如果大于0我们会做一件事情,小于0我们又处理其他事情。

这样的情况通过double计算出来的结果去和0比较大小,尤其是有小数点的时候,经常会因为精度丢失而导致程序处理流程出错。

首先贴一个使用的代码:

/**

*将double类型数据转为字符串(如将18.4转为1840,如果需要1840.0,把int强转去掉即可)

*@paramd

*@return

*/

publicstaticStringdouble2String(doubled){

BigDecimalbg=newBigDecimal(d*100);

doubledoubleValue=bg.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();

return

String.valueOf((int)doubleValue);

}

BigDecimal在《EffectiveJava》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。

BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个,它们是:

BigDecimal(doubleval)

TranslatesadoubleintoaBigDecimal.

BigDecimal(Stringval)

TranslatestheStringrepresentationofaBigDecimalintoaBigDecimal.上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。

我们可能想都不想就用上了,会有什么问题呢?

等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段:

Note:

theresultsofthisconstructorcanbesomewhatunpredictable.OnemightassumethatnewBigDecimal(.1)isexactlyequalto.1,butitisactuallyequalto.***-********-*****0**********7021********-*****.Thisissobecause.1cannotberepresentedexactlyasadouble(or,forthatmatter,asabinaryfractionof

anyfinitelength).Thus,thelongvaluethatisbeingpassedintotheconstructorisnotexactlyequalto.1,appearancesnonwithstanding.

The(String)constructor,ontheotherhand,isperfectlypredictable:

newBigDecimal(".1")isexactlyequalto.1,asonewouldexpect.Therefore,itisgenerallyrecommendedthatthe(String)constructorbeusedinpreferencetothisone.原来我们如果需要精确计算,非要用String来够造BigDecimal不可!

在《EffectiveJava》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。

解决现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。

但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。

你能够忍受这么烦琐的过程吗?

下面我们提供一个工具类Arith来简化操作。

它提供以下静态方法,包括加减乘除和四舍五入:

add(doublev1,doublev2)sub(doublev1,doublev2)mul(doublev1,doublev2))div(doublev1,doublev2,intscale)publicstaticdoubleround(doublev,intscale)

所以一般对double类型进行运算时,做好对结果进行处理,然后拿这个值去做其他事情。

使用如下:

/**

对double数据进行取精度.

value

double数据.

scale

精度位数(保留的小数位数).

paramroundingMode

精度取值方式.

*@return精度计算后的数据.

*/

publicstaticdoubleround(doublevalue,intscale,

introundingMode){

BigDecimalbd=newBigDecimal(value);

bd=bd.setScale(scale,roundingMode);

doubled=bd.doubleValue();

bd=null;

returnd;

}

/**

double相加

1

paramd2

*@return

*/

publicdoublesum(doubled1,doubled2){

11

BigDecimalbd2=newBigDecimal(Double.toString(d2));

returnbd1.add(bd2).doubleValue();

}

/**

double相减

1

paramd2

*@return

*/

publicdoublesub(doubled1,doubled2){

11

BigDecimalbd2=newBigDecimal(Double.toString(d2));

returnbd1.subtract(bd2).doubleValue();

}

/**

double乘法

1

paramd2

*@return

*/

publicdoublemul(doubled1,doubled2){

11

BigDecimalbd2=newBigDecimal(Double.toString(d2));

returnbd1.multiply(bd2).doubleValue();

}

/**

double除法

1

*@paramd2

paramscale四舍五入小数点位数

*@return

*/

publicdoublediv(doubled1,doubled2,intscale){

当然在此之前,你要判断分母是否为0,

//

为0你可以根据实际需求做相应的处理

11

BigDecimalbd2=newBigDecimal(Double.toString(d2));

returnbd1.divide

(bd2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

}

这样,计算double类型的数据计算问题就可以处理了。

另外补充一下JavaScript四舍五入的方法:

小数点问题

Math.round(totalAmount*100)/100(保留2位)

functionformatFloat(src,pos)

{

returnMath.round(src*Math.pow(10,pos))/Math.pow(10,pos);

}

四舍五入是我们小学的数学问题,这个问题对于我们程序猿来说就类似于1到10的加减乘除那么简单了。

在讲解之间我们先看如下一个经典的案例:

[java]viewplaincopyprint?

1publicstaticvoidmaiString[]args){

2

12.5的四舍五入值:

"+Math.round(12.5));

3

System.out.println("-12.5的四舍五入值:

"+Math.round(-12.5));

4

}

5Output:

612.5的四舍五入值:

13

7.-12.5的四舍五入值:

-12

这是四舍五入的经典案例,也是我们参加校招时候经常会遇到的(貌似我参加笔试的时候遇到过好多次)。

从这儿结果中我们发现这两个绝对值相同的数字,为何近似值会不同呢?

其实这与Math.round采用的四舍五入规则来决定。

四舍五入其实在金融方面运用的非常多,尤其是银行的利息。

我们都知道银行的盈利渠道主要是利息差,它从储户手里收集资金,然后放贷出去,期间产生的利息差就是银行所获得的利润。

如果我们采用平常四舍五入的规则话,这里采用每10笔存款利息计算作为模型,如下:

四舍:

0.000、0.001、0.002、0.003、0.004。

这些舍的都是银行赚的钱。

五入:

0.005、0.006、0.007、0.008、0.009。

这些入的都是银行亏的钱,分别为:

0.005、0.004、.003、0.002、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-----五后无数,若前位为奇数应进位

下面实例是使用银行家舍入法:

[java]viewplaincopyprint?

1publicstaticvoidmain(String[]args){

2

d=newBigDecimal(*****);

//存款

3.

BigDecimalr=newBigDecimal(0.001875*3);

//利息

4.

BigDecimali=d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);

//使用银行家算法

5

6

System.out.println("季利息是:

"+i);

7

}

8Output:

9.季利息是:

562.50

在上面简单地介绍了银行家舍入法,目前java支持7中舍入法:

1、ROUND_UP:

远离零方向舍入。

向绝对值最大的方向舍入,只要舍弃位非0即进位。

2、ROUND_DOWN:

趋向零方向舍入。

向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。

3、ROUND_*****:

向正无穷方向舍入。

向正最大方向靠拢。

若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。

Math.round()方法就是使用的此模式。

4、ROUND_FLOOR:

向负无穷方向舍入。

向负无穷方向靠拢。

若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。

5、HALF_UP:

最近数字舍入(5进)。

这是我们最经典的四舍五入。

6、HALF_DOWN:

最近数字舍入(5舍)。

在这里5是要舍弃的。

7、HAIL_EVEN:

银行家舍入法。

提到四舍五入那么保留位就必不可少了,在java运算中我们可以使用多种方式来实现保留位。

保留位

方法一:

四舍五入方法一:

四舍五入

[java]viewplaincopyprint?

1double

f

=

*****.5585;

2BigDecimal

b

=

new

BigDecimal(f);

3.double

f1

=

b.setScale(2,

RoundingMode.HALF_UP).doubleValue();

在这里使用BigDecimal,并且采用setScale方法来设置精确度,同时使用RoundingMode.HALF_UP表示使用最近数字舍入法则来近似计算。

在这里我们可以看出BigDecimal和四舍五入是绝妙的搭配。

方式二:

方式二:

[java]viewplaincopyprint?

1java.text.DecimalFormat

df

=new

java.text.DecimalFormat(”#.00″);

2.df.format(你要格式化的数字);

例:

newjava.text.DecimalFormat(”#.00″).format(3.***-*****)

#.00表示两位小数#.0000四位小数以此类推…

方式三:

方式三:

[java]viewplaincopyprint?

1doubled=3.***-*****;

2

3Stringresult=String.format(”%.2f”);

4

5.%.2f%.表示小数点前任意位数

2表示两位小数格式后的结果为f表示浮点型。

方式四:

方式四:

此外如果使用struts标签做输出的话,有个format属性,设置为format="0.00"就是保留两位小数

例如:

[java]viewplaincopyprint?

1bean:

writename="entity"property="dkhAFSumPl"

format="0.00"/

2

3或者

4

5.fmt:

formatNumbertype="number"value="${*****.22/100}"maxFractionDigits="0"/

6

7.maxFractionDigits表示保留的位数

BigDecimal.setScale处理java小数点BigDecimal.setScale()方法用于格式化小数点setScale

(1)表示保留一位小数,默认用四舍五入方式

DOWN)直接删除多余的小数位,如2.35会变成2.3

UP)进位处理,2.35变成2.4

(1,BigDecimal.ROUND_HALF_UP)四舍五入,2.35变成2.4setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五入,2.35变成2.3,如果是5则向下舍

注释:

注释:

1:

ale指的是你小数点后的位数。

比如123.456则score就是3.score()就是BigDecimal类中的方法啊。

比如:

BigDecimalb=newBigDecimal("123.456");b.scale(),返回的就是3.

2:

roundingMode是小数的保留模式。

它们都是BigDecimal中的常量字段,有很多种。

比如:

BigDecimal.ROUND_HALF_UP表示的就是4舍5入。

3:

pubilcBigDecimaldivide(BigDecimaldivisor,intscale,introundingMode)的意思是说:

我用一个BigDecimal对象除以divisor后的结果,并且要求这个结果保留有scale个小数位,roundingMode表示的就是保留模式是什么,是四舍五入啊还是其它的,你可以自己选!

4:

对于一般:

对于一般add、subtract、multiply方法的小数位格式化如下:

方法的小数位格式化如下:

BigDecimalmData=newBigDecimal("9.655").setScale(2,BigDecimal.ROUND_HALF_UP);

System.out.println("mData="+mData);

----结果:

结果:

-----mData=9.66

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 工作计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1