在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

BigDecimal浅析

2017-2-7 13:40| 发布者: zhangjf| 查看: 565| 评论: 0

摘要: 为什么使用BigDecimal 首先看一个例子:public class DoubleTest {public static void main(String args) { System.out.println(0.1 + 0.2); }}输出的结果:0.30000000000000004(我们的预期是0.3)其实float ...
为什么使用BigDecimal
  1. 首先看一个例子:
  2. public class DoubleTest {
  3. public static void main(String[] args) {
  4. System.out.println(0.1 + 0.2);
  5. }
  6. }
  7. 输出的结果:0.30000000000000004(我们的预期是0.3)
  8. 其实float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。所有就出现了BigDecimal。
  9. jdk文档BigDecimal的描述:
  10. 不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值和32位的整数标度(scale)组成。BigDecimal 表示的数值是 (unscaledValue × 10-scale)。
复制代码
BigDecial构造方法
  1. 下面是常用的四种构造方法:
  2. BigDecimal(int)
  3. BigDecimal(double)
  4. BigDecimal(long)
  5. BigDecimal(String)
  6. public static void main(String[] args) {
  7. double a = 123.11;
  8. BigDecimal bigDecimal = new BigDecimal(a);
  9. System.out.println(bigDecimal);
  10. }
  11. 输出结果:123.1099999999999994315658113919198513031005859375
  12. public static void main(String[] args) {
  13. String a = "123.11";
  14. BigDecimal bigDecimal = new BigDecimal(a);
  15. System.out.println(bigDecimal);
  16. }
  17. 输出结果:123.11
  18. public static void main(String[] args) {
  19. double a = 123.11;
  20. System.out.println(BigDecimal.valueOf(a));
  21. }
  22. 输出结果:123.11
  23. BigDecimal(double)这个构造方法为什么会输出不是我们预期的值:
  24. 查看源码的注释得知: new BigDecimal(0.1)所创建的BigDecimal实际上等于 0.1000000000000000055511151231257827021181583404541015625,这是因为0.1无法准确地表示为 double(或者不能表示为任何有限长度的二进制小数),所以建议使用BigDecimal(String)构造方法。
  25. valueOf其实也调用BigDecimal(String)构造方法
  26. public static BigDecimal valueOf(double val) {
  27. // Reminder: a zero double returns '0.0', so we cannot fastpath
  28. // to use the constant ZERO. This might be important enough to
  29. // justify a factory approach, a cache, or a few private
  30. // constants, later.
  31. return new BigDecimal(Double.toString(val));
  32. }
复制代码
BigDecimal基本运算
  1. add(BigDecimal) BigDecimal对象中的值相加
  2. subtract(BigDecimal) BigDecimal对象中的值相减
  3. multiply(BigDecimal) BigDecimal对象中的值相乘
  4. divide(BigDecimal) BigDecimal对象中的值相除
  5. 注意:+-* /运算返回的是新的BigDecimal对象
  6. 比较大小: compareTo()
  7. -1、0、1,即左边比右边数大,返回1,相等返回0,比右边小返回-1。注意不能使用equals方法来比较大小。
  8. //add(BigDecimal)源码
  9. public BigDecimal add(BigDecimal augend) {
  10. if (this.intCompact != INFLATED) {
  11. if ((augend.intCompact != INFLATED)) {
  12. return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
  13. } else {
  14. return add(this.intCompact, this.scale, augend.intVal, augend.scale);
  15. }
  16. } else {
  17. if ((augend.intCompact != INFLATED)) {
  18. return add(augend.intCompact, augend.scale, this.intVal, this.scale);
  19. } else {
  20. return add(this.intVal, this.scale, augend.intVal, augend.scale);
  21. }
  22. }
  23. }
  24. //add
  25. private static BigDecimal add(final long xs, int scale1, BigInteger snd, int scale2) {
  26. int rscale = scale1;
  27. long sdiff = (long)rscale - scale2;
  28. boolean sameSigns = (Long.signum(xs) == snd.signum);
  29. BigInteger sum;
  30. if (sdiff < 0) {
  31. int raise = checkScale(xs,-sdiff);
  32. rscale = scale2;
  33. long scaledX = longMultiplyPowerTen(xs, raise);
  34. if (scaledX == INFLATED) {
  35. sum = snd.add(bigMultiplyPowerTen(xs,raise));
  36. } else {
  37. sum = snd.add(scaledX);
  38. }
  39. } else { //if (sdiff > 0) {
  40. int raise = checkScale(snd,sdiff);
  41. snd = bigMultiplyPowerTen(snd,raise);
  42. sum = snd.add(xs);
  43. }
  44. return (sameSigns) ?
  45. //新对象
  46. new BigDecimal(sum, INFLATED, rscale, 0) :
  47. //valueOf()返回的也是新对象
  48. valueOf(sum, rscale, 0);
  49. }
复制代码
BigDecimal格式化

如果我们要将String转换为BigDecimal,然后保留两位小数。

  1. DecimalFormat format = new DecimalFormat("0.00");
  2. System.out.println(format.format(new BigDecimal(str)));
  3. //如果四舍五入呢
  4. String str = "123.4444";
  5. System.out.println(new BigDecimal(str).setScale(2,BigDecimal.ROUND_HALF_UP));
  6. 参数
  7. 直接删除多余的小数位,如2.35会变成2.3 setScale(1,BigDecimal.ROUND_DOWN)
  8. 进位处理,2.35变成2.4 setScale(1,BigDecimal.ROUND_UP)
  9. 四舍五入,2.35变成2.4 setScale(1,BigDecimal.ROUND_HALF_UP)
  10. 四舍五入,2.35变成2.3,如果是5则向下舍setScaler(1,BigDecimal.ROUND_HALF_DOWN)
复制代码
BigDecimal使用时注意点

(1)尽量使用参数类型为String的构造函数。

(2) BigDecimal都是不可变的,每次计算会产生新的对象,所以+-*/后保存值,如:a.dd(b)要写成a = a.add(b)。

BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成理解
  1. 例如:-314和 3.1415
  2. 表示为:-314 × 10-0 和13412 × 10-4
  3. 这里用(非标度值 和 标度)表示分别为:[-314, 0]和[13412, 4]
  4. BigDecimal amount = new BigDecimal("314");
  5. System.out.println(amount.signum());//正负
  6. System.out.println(amount.scale()); //标度
  7. System.out.println(amount.stripTrailingZeros());
  8. System.out.println(amount.stripTrailingZeros().scale());//去零后的标度(注意是末尾的0)
  9. //结果
  10. 1
  11. 0
  12. 314
  13. 0
  14. BigDecimal amount = new BigDecimal("3.1415");
  15. System.out.println(amount.signum());//正负
  16. System.out.println(amount.scale()); //标度
  17. System.out.println(amount.stripTrailingZeros());
  18. System.out.println(amount.stripTrailingZeros().scale());//去零后的标度
  19. //结果
  20. 1
  21. 4
  22. 3.1415
  23. 4
  24. //大家尝试理解判断是否是整数这段代码
  25. private boolean isIntegerValue(BigDecimal bd) {
  26. return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0;
  27. }
复制代码




来自: http://my.oschina.net/u/2361475/blog/598519

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-9 10:32

Copyright 2015-2025 djqfx

返回顶部