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

前言

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

问题描述:

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

 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方法源码如下:

 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方法会判断数字的精度,看下面的代码及运行结果:

 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的方法,通过注释可知,貌似可以进行不同精度的比较,看下面的代码。

 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对应的字段,如下:

<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底层是如何实现的吧,以免以后犯类似的错误嘛。

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

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;
 }
...
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是否正确咋个办?

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

 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

总结

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

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

(0)

相关推荐

  • java中BigDecimal进行加减乘除的基本用法

    前言 众所周知Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更小的数进行运算和处理.float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal. BigDecimal所创建的是对象,我们不能使用传统的+.-.*./等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法. 方法中的参数也必须是B

  • Java编程BigDecimal用法实例分享

    Java中提供了大数字(超过16位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecimal 类,用于高精度计算. 其中 BigInteger 类是针对大整数的处理类,而 BigDecimal 类则是针对大小数的处理类. BigDecimal 类的实现用到了 BigInteger类,不同的是 BigDecimal 加入了小数的概念. float和Double只能用来做科学计算或者是工程计算;在商业计算中,对数字精度要求较高,必须使用 BigIn

  • Java中使用BigDecimal进行浮点数运算

    最近研究了一下Java的浮点数计算问题,从网上查询了相关的资料,汇总并经过了一些整理和调试,最后完成此文,欢迎大家指出其中的错误和问题. 在Java中,float声明的变量是单精度浮点数,double声明的变量是双精度浮点数,顾名思义就是double型的实体占用内存空间是float的两倍.float是4个字节而double是8个字节.float和double类型的数据,无法精确表示计算结果,这是由于float和double是不精确的计算.大家可以通过下面代码可以看出来: 复制代码 代码如下: p

  • Java中BigDecimal的加减乘除、比较大小与使用注意事项

    前言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,在java 里面,int 的最大值是:2147483647,现在如果想用比这个数大怎么办?换句话说,就是数值较大,这时候就用到了BigDecimal ,关于BigDecimal 的介绍

  • java实现大数加法(BigDecimal)的实例代码

    复制代码 代码如下: import java.util.*;import java.io.*;import java.lang.String;import java.math.BigDecimal; public class p1036{    public static void main(String[] args)    {        String s1,s2;        Scanner cin = new Scanner(System.in);                s1

  • Java中BigDecimal类的简单用法

    本文实例讲述了Java中BigDecimal类的简单用法,是Java程序设计中非常实用的技巧,分享给大家供大家参考.具体用法分析如下: 一般来说,一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法进行精确计算.但是Java的设计者给编程人员提供了一个很有用的类BigDecimal,他可以完善float和double类无法进行精确计算的缺憾.BigDecimal类位于java.maths类包下.首先我们来看下如何构造一个BigDecimal对象.它的构造函数很多,

  • Java BigDecimal类用法详解

    1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦. 2.BigDecimal简介 BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成.如果为零或正数

  • java中BigDecimal的操作方法

    本文实例讲述了java中BigDecimal的操作方法.分享给大家供大家参考.具体分析如下: 由于double,float的精度不够,因此在进行商业计算的时候要使用的BigDecimal.BigDecimal对象创建如下: 复制代码 代码如下: BigDecimal b = new BigDecimal("12.000001"); System.out.println(b); 输出结果为:12.000001: BigDecimal在创建的时候可以传入String和double,但是最好

  • Java BigDecimal类的使用和注意事项

    BigDecimal简介 JDK文档(中文)中的解释如下: 不可变的.任意精度的有符号十进制数.BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成.如果为零或正数,则标度是小数点后的位数.如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂.因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale). 具体解释 1."BigDecimal 对象的值是不可变的".这点在BigDecimal 对象的运

  • 使用BigDecimal进行精确运算(实现加减乘除运算)

    首先我们先来看如下代码示例: 复制代码 代码如下: public class Test_1 {public static void main(String[] args) {System.out.println(0.06+0.01);System.out.println(1.0-0.42);System.out.println(4.015*100);System.out.println(303.1/1000);}} 运行结果如下0.06999999999999999 0.580000000000

随机推荐