您的位置:首页 > 其它

Bigdecimal进行精确计算

2015-12-23 18:11 239 查看
由于JDK中对于float和double处理机制带来了天然的精度损失,所以在商业应用中一般都采用Bigdecimal进行精确计算

但是Bigdecimal在使用过程中仍然存在做一些以前未注意的坑,在此做些记录备忘

Bigdecimal是不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale)。

由于Bigdecimal是不可变的,因此每次对于BigDecimal的操作都会产生一个新的对象,后续操作我们需要拿着这个新的对象进行操作

BigDecimal a = new BigDecimal(998);
a.add(new BigDecimal(2));
System.out.println(a);


这种操作是错误的,a的值仍为998,我们应该对a重新赋值 a = a.add(new BigDecimal(2));

BigDecimal 类使用户能完全控制舍入行为。如果未指定舍入模式,并且无法表示准确结果,则抛出一个异常。因此当我们进行除法操作(divide)的时候需要指定舍入模式和精度
divide(BigDecimal divisor, int scale, int roundingMode)


Bigdecimal本身提供了多种舍入方式

static int ROUND_CEILING
接近正无穷大的舍入模式。
static int ROUND_DOWN
接近零的舍入模式。
static int ROUND_FLOOR
接近负无穷大的舍入模式。
static int ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
static int ROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
static int ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
static int ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。
static int ROUND_UP
舍入远离零的舍入模式。


Bigdecimal本身通过setScale的方法进行标度的设置

setScale
public BigDecimal setScale(int newScale)返回一个 BigDecimal,其标度为指定值,其值在数值上等于此 BigDecimal 的值。如果这不可能,则抛出 ArithmeticException。
此调用通常用于增加标度,在这种情况下,可以保证存在指定标度和正确值的 BigDecimal。如果调用方知道 BigDecimal在其小数部分的结尾有足够多的零(即其整数值中的十的因子),则该调用也可用于减少标度,以允许重新标度,而不更改其值。

此方法返回与 setScale 的两个参数版本相同的结果,但是,为调用方省去了指定舍入模式的麻烦(舍入模式不影响结果)。

注意,由于 BigDecimal 对象是不可变的,因此此方法的调用不会 导致初始对象被修改,这与使用名为 setX 变异字段 X 方法的常规约定相反。相反,setScale 返回具有适当标度的对象;返回的对象不一定是新分配的。


需要注意setScale如果不指定Round方式可能会抛异常,因为进行了舍入操作。因此通常使用api:

public BigDecimal setScale(int newScale,int roundingMode)


一般来说,可以使用BigDecimal的构造方法或者静态方法的valueOf()方法把基本类型的变量构建成BigDecimal对象。

但是我们需要注意一点当使用构造函数操作double或者float数据的时候

BigDecimal a= new BigDecimal(1.0);


此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法

当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用
Double.toString(double)
方法,然后使用
BigDecimal(String)
构造方法,将 double 转换为 String。要获取该结果,请使用 static
valueOf(double)
方法。

注意,如果字符序列已经可以作为一个字符数组使用,则使用public BigDecimal(char[] in)构造方法要比将 char 数组转换为字符串并使用 BigDecimal(String) 构造方法更快。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: