好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

因BigDecimal类型数据引出的问题详析

前言

我们都知道,java中对大小数,高精度的计算都会用到bigdecimal.但是在实际应用中,运用bigdecimal还是会遇到一些问题,下面话不多说了,来一起看看详细的介绍吧

问题描述:

程序中需要判断一个字段是否为0(字段 类型 为bigdecimal),想都没想,对象的判断用equals?结果却与预期有一定的差距,看下面代码及运行结果。

?

1

2

3

4

5

public static void main(string[] args) {

  bigdecimal decimal1 = bigdecimal.valueof( 0 );

  bigdecimal decimal2 = new bigdecimal( "0.00" );

  system.out.println( "the result is " +decimal1.equals(decimal2));

}

运行结果:

the result is false

结论: bigdecimal类型比较相等不能简单的通过equals方法实现。

bigdecimal类的equals方法源码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public boolean equals(object x) {

  if (!(x instanceof bigdecimal))

   return false ;

  bigdecimal xdec = (bigdecimal) x;

  if (x == this )

   return true ;

  if (scale != xdec.scale) //这里会比较数字的精度

   return false ;

  long s = this .intcompact;

  long xs = xdec.intcompact;

  if (s != inflated) {

   if (xs == inflated)

    xs = compactvalfor(xdec.intval);

   return xs == s;

  } else if (xs != inflated)

   return xs == compactvalfor( this .intval);

 

  return this .inflate().equals(xdec.inflate());

}

看上面的注释可以知道,bigdecimal类的equals方法会判断数字的精度,看下面的代码及运行结果:

?

1

2

3

4

5

public static void main(string[] args) {

  bigdecimal decimal1 = bigdecimal.valueof( 0 ).setscale( 2 );

  bigdecimal decimal2 = new bigdecimal( "0.00" ).setscale( 2 );

  system.out.println( "the result is " +decimal1.equals(decimal2));

}

运行结果:

the result is true

结论: 使用bigdecimal类equals方法判断两个bigdecimal类型的 数据 时,需要设置精度,否则结果可能不正确。

思考:每次都设置精度比较麻烦,有其他方式进行相等的比较吗?

看了下bigdecimal的方法列表,有一个名为compareto的方法,通过注释可知,貌似可以进行不同精度的比较,看下面的代码。

?

1

2

3

4

5

public static void main(string[] args) {

  bigdecimal decimal1 = bigdecimal.valueof( 1.1 );

  bigdecimal decimal2 = new bigdecimal( "1.10" );

  system.out.println( "the result is " +decimal1.compareto(decimal2));

}

运行结果:

the result is 0

0表示两个数相等,所有可以通过compareto实现不同精度的两个bigdecimal类型的数字是否相等的比较

引出的问题:公司的项目中,为了避免由于精度丢失引起问题,凡是有精度要求的字段用的都是bigdecimal类型。数据持久层用的是mybatis框架,mybatis的mapper文件中有些条件判断用的是bigdecimal对应的字段,如下:

?

1

2

3

4

5

6

<select id= "selectbycondition" resulttype= "com.scove.demo.domain.score" >

  select * from tb_score where 1 = 1

  < if test= "score!=null and score!=0" >

   and score>#{score}

  </ if >

  ...

score是一个bigdecimal类型的字段,score!=0 mybatis是如何进行判断的,会不会用的是上面的equals方法?如果是那么项目上线会不会捅大篓子,想到这儿,有点怕了。写了个程序测了下,这样写完全没问题,能够达到我想要的目的。但是还是有点担心,看看mybatis底层是如何实现的吧,以免以后犯类似的错误嘛。

经过分析调试,很快就找到了关键代码的位置,如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

public class expressionevaluator {

 

  public boolean evaluateboolean(string expression, object parameterobject) {

  object value = ognlcache.getvalue(expression, parameterobject);

  if (value instanceof boolean ) {

   return ( boolean ) value;

  }

  if (value instanceof number) {

   return ! new bigdecimal(string.valueof(value)).equals(bigdecimal.zero);

  }

  return value != null ;

  }

...

?

1

2

3

4

5

6

7

8

9

10

11

public final class ognlcache {

 

....

  public static object getvalue(string expression, object root) {

  try {

   map<object, ognlclassresolver> context = ognl.createdefaultcontext(root, new ognlclassresolver());

   return ognl.getvalue(parseexpression(expression), context, root);

  } catch (ognlexception e) {

   throw new builderexception( "error evaluating expression '" + expression + "'. cause: " + e, e);

  }

  }

用的是表达式求值,ognl这个类的竟然没有源码,apache的官网上找了下,是有相应的源码的,只是需要单独下载,真麻烦,算了不看了。据说底层用的是spring 的ognl表达式,我也不 关心了。但是以后如果不确定mapper中的test是否正确咋个办?

想了下,还是写一个工具类在拿不准的时候用一下吧,反正拿不准的时候肯定很少,所以随便写了简单的吧,如下:

?

1

2

3

4

5

6

7

8

public static void main(string[] args) {

  expressionevaluator evaluator = new expressionevaluator();

  string expression = "score!=null and score!=0" ;

  dynamiccontext context = new dynamiccontext( new configuration(), null );

  context.bind( "score" , bigdecimal.valueof( 0.1 ));

  boolean flag = evaluator.evaluateboolean(expression , context.getbindings());

  system.out.println( "the result is " +flag);

}

运行结果:

the result is true

总结

开发过程中,一定要细心去处理细节上的东西,不然一不小心就跳坑里了,轻则系统造成数据的不一致,修复数据;重则造成重大的损失....

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

原文链接:https://www.cnblogs.com/falco/p/9500833.html

查看更多关于因BigDecimal类型数据引出的问题详析的详细内容...

  阅读:52次