查看: 756|回复: 0

[手机开发] 不要相信 Double!

发表于 2018-5-9 08:00:03

在Java浮点类型中,经常会使用 Double 类型。在大部分场景,使用
Double 并不会有什么问题。但如果是数值计算,就要小心了。

眼见不一定为实

如果我们编译运行下面这个程序会看到什么?

  1. public class Test{
  2. public static void main(String args[]){
  3. System.out.println(0.05+0.01);
  4. System.out.println(1.0-0.42);
  5. System.out.println(4.015*100);
  6. System.out.println(123.3/100);
  7. }
  8. };
复制代码

也许你脑子马上有了答案,但对不起,你的答案多半不准确,因为结果确实是:

  1. 0.060000000000000005
  2. 0.5800000000000001
  3. 401.49999999999994
  4. 1.2329999999999999
复制代码

不光是Java,在其它很多编程语言中也有这样的问题。Java中的简单浮点数类型float和double进行运算时,并不能有一个精确的结果。这是计算机本身的性质决定的。

使用 BigDecimal

好在 Java 提供了BigDecimal,可以做相应的转换。参考下面的工具类:

  1. import java.math.BigDecimal;
  2. /**
  3. * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精
  4. * 确的浮点数运算,包括加减乘除和四舍五入。
  5. */
  6. public class Arith{
  7. //默认除法运算精度
  8. private static final int DEF_DIV_SCALE = 10;
  9. //这个类不能实例化
  10. private Arith(){
  11. }
  12. /**
  13. * 提供精确的加法运算。
  14. * @param v1 被加数
  15. * @param v2 加数
  16. * @return 两个参数的和
  17. */
  18. public static double add(double v1,double v2){
  19. BigDecimal b1 = new BigDecimal(Double.toString(v1));
  20. BigDecimal b2 = new BigDecimal(Double.toString(v2));
  21. return b1.add(b2).doubleValue();
  22. }
  23. /**
  24. * 提供精确的减法运算。
  25. * @param v1 被减数
  26. * @param v2 减数
  27. * @return 两个参数的差
  28. */
  29. public static double sub(double v1,double v2){
  30. BigDecimal b1 = new BigDecimal(Double.toString(v1));
  31. BigDecimal b2 = new BigDecimal(Double.toString(v2));
  32. return b1.subtract(b2).doubleValue();
  33. }
  34. /**
  35. * 提供精确的乘法运算。
  36. * @param v1 被乘数
  37. * @param v2 乘数
  38. * @return 两个参数的积
  39. */
  40. public static double mul(double v1,double v2){
  41. BigDecimal b1 = new BigDecimal(Double.toString(v1));
  42. BigDecimal b2 = new BigDecimal(Double.toString(v2));
  43. return b1.multiply(b2).doubleValue();
  44. }
  45. /**
  46. * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
  47. * 小数点以后10位,以后的数字四舍五入。
  48. * @param v1 被除数
  49. * @param v2 除数
  50. * @return 两个参数的商
  51. */
  52. public static double div(double v1,double v2){
  53. return div(v1,v2,DEF_DIV_SCALE);
  54. }
  55. /**
  56. * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
  57. * 定精度,以后的数字四舍五入。
  58. * @param v1 被除数
  59. * @param v2 除数
  60. * @param scale 表示表示需要精确到小数点以后几位。
  61. * @return 两个参数的商
  62. */
  63. public static double div(double v1,double v2,int scale){
  64. if(scale<0){
  65. throw new IllegalArgumentException(
  66. "The scale must be a positive integer or zero");
  67. }
  68. BigDecimal b1 = new BigDecimal(Double.toString(v1));
  69. BigDecimal b2 = new BigDecimal(Double.toString(v2));
  70. return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
  71. }
  72. /**
  73. * 提供精确的小数位四舍五入处理。
  74. * @param v 需要四舍五入的数字
  75. * @param scale 小数点后保留几位
  76. * @return 四舍五入后的结果
  77. */
  78. public static double round(double v,int scale){
  79. if(scale<0){
  80. throw new IllegalArgumentException(
  81. "The scale must be a positive integer or zero");
  82. }
  83. BigDecimal b = new BigDecimal(Double.toString(v));
  84. BigDecimal one = new BigDecimal("1");
  85. return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
  86. }
  87. }; ?
复制代码


回复

使用道具 举报