double转BigDecimal的注意事项

/

2019-10-17

不要直接用double变量作为构造BigDecimal的参数。
线上有这么一段Java代码逻辑:

1,接口传来一个JSON串,里面有个数字:57.3。

2,解析JSON并把这个数字保存在一个float变量。

3,把这个float变量赋值给一个 BigDecimal对象,用的是BigDecimal的double参数的构造:

  1. new BigDecimal(double val)

4,把这个BigDecimal保存到MySQL数据库,字段类型是decimal(15,2)。
这段代码逻辑在线上跑了好久了,数据库保存的值是57.3也没什么问题,但是在今天debug的时候发现,第三步的BigDecimal对象保存的值并不是57.3,而是57.299999237060546875,很明显,出现了精度的问题。

至于数据库最终保存了正确的57.3完全是因为字段类型设置为2位小数,超过2位小数就四舍五入,所以才得到了正确的结果,相当于MySQL给我们把这个精度问题掩盖了。
总觉得这是个坑,所以研究了一下相关的知识。

首先是BigDecimal的double参数构造,在官方JDK文档中对这个构造是这么描述的:
public BigDecimal(double val)

1,BigDecimal(double val)构造,用double当参数来构造一个BigDecimal对象。

2,但是这个构造不太靠谱(unpredictable),你可能以为BigDecimal(0.1)就是妥妥的等于0.1,但是你以为你以为的就是你以为的?还真不是,BigDecimal(0.1)这货实际上等于0.1000000000000000055511151231257827021181583404541015625,因为准确的来说0.1本身不能算是一个double(其实0.1不能代表任何一个定长二进制分数)。

3,BigDecimal(String val)构造是靠谱的,BigDecimal(“0.1”)就是妥妥的等于0.1,推荐大家用这个构造。

4,如果你非得用一个double变量来构造一个BigDecimal,没问题,我们贴心的提供了静态方法valueOf(double),这个方法跟new Decimal(Double.toString(double))效果是一样的。

说白了就是别直接拿double变量做参数,最好使用String类型做参数或者使用静态方法valueOf(double),我写了个例子试了一下:

  1. public static void main(String[] args) {
  2. float a=57.3f;
  3. BigDecimal decimalA=new BigDecimal(a);
  4. System.out.println(decimalA);
  5. double b=57.3;
  6. BigDecimal decimalB=new BigDecimal(b);
  7. System.out.println(decimalB);
  8. double c=57.3;
  9. BigDecimal decimalC=new BigDecimal(Double.toString(c));
  10. System.out.println(decimalC);
  11. double d=57.3;
  12. BigDecimal decimalD=BigDecimal.valueOf(d);
  13. System.out.println(decimalD);
  14. }

输出结果:

57.299999237060546875

57.2999999999999971578290569595992565155029296875

57.3

57.3
以后还是尽量按照官方推荐的套路来,否则不知道什么时候又给自己挖坑了。

Reproduced please indicate the author and the source, and error a link to this page.
text link: /23.html

不要让懒惰占据你的大脑,不让要妥协拖跨你的人生。青春就是一张票,能不能赶上时代的快车,你的步伐掌握在你的脚下,good luck3
2019/7/29上线