美文网首页Java 杂谈
BigDecimal equals方法可能不相等

BigDecimal equals方法可能不相等

作者: 朱端的一坨 | 来源:发表于2018-04-28 15:42 被阅读28次

    好久也没有写点什么了,虽然一直想根据某个切入点写一篇比较完整的文章,但是总是没有抽空写。正好有点空闲,姑且就先写个最近遇到的小问题吧,也是做个备忘。

    背景

    正好之前有写过一个小工具,用来比对对象之间的差异 对比小工具 ,最近在项目的使用过程中,突然发现对于处理BigDecimal的时候存在一个问题,就是对于 0 和 0.00 认为他们是不一样的。
    虽然主要的问题还是我们在从数据库读取值以及设置默认值的时候,对于精度(scale)的处理不一致导致的,不过分析了BigDecimal的equals方法,也觉得很有意思。

    问题原因

    其实在BigDecimal的实现类里面注释已经说的很清楚了,不过如果不注意看的话,还是很容易忽略掉这个细节的。
    具体来说就是BigDecimal的Equals方法,在对比的时候是要比较scale的,也就是说如果scale不相等,也是会返回false的。比如0和0.00,因为两者的scale是不同的,所以结果自然是false。
    不得不说,确实从Decimal的设计原理上来说,确实这个考虑也是合理的,因此在使用的时候我们还是需要多多注意的。

        /**
         * Compares this {@code BigDecimal} with the specified
         * {@code Object} for equality.  Unlike {@link
         * #compareTo(BigDecimal) compareTo}, this method considers two
         * {@code BigDecimal} objects equal only if they are equal in
         * value and scale (thus 2.0 is not equal to 2.00 when compared by
         * this method).
         *
         * @param  x {@code Object} to which this {@code BigDecimal} is
         *         to be compared.
         * @return {@code true} if and only if the specified {@code Object} is a
         *         {@code BigDecimal} whose value and scale are equal to this
         *         {@code BigDecimal}'s.
         * @see    #compareTo(java.math.BigDecimal)
         * @see    #hashCode
         */
        @Override
        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.inflated().equals(xDec.inflated());
        }
    

    那么对于这种本身就需要忽略scale的对比怎么办?其实BigDecimal类也提供了相关的compare方法,而且这个方法的设计也和comparable接口的实现也很相似,所以使用起来也挺舒服的。

        /**
         * Compares this {@code BigDecimal} with the specified
         * {@code BigDecimal}.  Two {@code BigDecimal} objects that are
         * equal in value but have a different scale (like 2.0 and 2.00)
         * are considered equal by this method.  This method is provided
         * in preference to individual methods for each of the six boolean
         * comparison operators ({@literal <}, ==,
         * {@literal >}, {@literal >=}, !=, {@literal <=}).  The
         * suggested idiom for performing these comparisons is:
         * {@code (x.compareTo(y)} &lt;<i>op</i>&gt; {@code 0)}, where
         * &lt;<i>op</i>&gt; is one of the six comparison operators.
         *
         * @param  val {@code BigDecimal} to which this {@code BigDecimal} is
         *         to be compared.
         * @return -1, 0, or 1 as this {@code BigDecimal} is numerically
         *          less than, equal to, or greater than {@code val}.
         */
        public int compareTo(BigDecimal val) {
            // Quick path for equal scale and non-inflated case.
            if (scale == val.scale) {
                long xs = intCompact;
                long ys = val.intCompact;
                if (xs != INFLATED && ys != INFLATED)
                    return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
            }
            int xsign = this.signum();
            int ysign = val.signum();
            if (xsign != ysign)
                return (xsign > ysign) ? 1 : -1;
            if (xsign == 0)
                return 0;
            int cmp = compareMagnitude(val);
            return (xsign > 0) ? cmp : -cmp;
        }
    

    解决方案

    由于很多场景下我们都是在算费等场景下使用BigDecimal,所以在比对BigDecimal的时候,尽量使用compare方法(忽略scale)来比较,而非equals方法,可能更符合我们的需求。

    p.s. 最后既然说到了scale的问题,也顺便提一下关于precision的一个小例子,相信如果明白了为什么需要使用BigDecimal而非Double的小伙伴们一定会会心一笑的。

    double sum = 0.0d;
    for( int i=0; i<100; i++ ){
        sum = sum + 0.01;
    }
    
    // 结果是 true 还是 false ?
    System.out.println("result is " + (sum==1.00d) );
    

    相关文章

      网友评论

        本文标题:BigDecimal equals方法可能不相等

        本文链接:https://www.haomeiwen.com/subject/wiuglftx.html