Why can't BigDecimal's equals method be used for equivalence comparison

Posted by KoA on Thu, 03 Mar 2022 03:49:56 +0100

preface

BigDecimal is Java A type provided in the math package that can be used for precise operations. Therefore, BigDecimal is frequently used in payment, e-commerce and other businesses. Moreover, it has many internal methods, such as addition, subtraction, multiplication, division and other operation methods, which can be called directly. In addition to using BigDecimal to represent numbers and carry out numerical operations, the code often needs to judge the equality of numbers.

So why is there such a requirement 🤔~ What is the secret 🤔~ Please listen to me

BigDecimal for equivalence comparison

    public static void main(String[] args) {
        BigDecimal bigDecimal1 = new BigDecimal(1);
        BigDecimal bigDecimal2 = new BigDecimal(1);

        if(bigDecimal1 == bigDecimal2){
            //Equivalent comparison
        }
    }

I believe that smart partners can see at a glance that the above code is problematic, because BigDecimal is an object and = = cannot be used for equivalence judgment.

Can we use BigDecimal's equals method for equivalence comparison? 👇

    public static void main(String[] args) {
        BigDecimal bigDecimal1 = new BigDecimal(1);
        BigDecimal bigDecimal2 = new BigDecimal(1);

        if(bigDecimal1.equals(bigDecimal2)){
            //Equivalent comparison
        }
    }

Let's run the code to see if we can use BigDecimal's equals method for equivalence comparison (● '◡' ●),

    public static void main(String[] args) {
        BigDecimal bigDecimal1 = new BigDecimal(1);
        BigDecimal bigDecimal2 = new BigDecimal(1);
        System.out.println(bigDecimal1.equals(bigDecimal2));

        BigDecimal bigDecimal3 = new BigDecimal(1);
        BigDecimal bigDecimal4 = new BigDecimal(1.0);
        System.out.println(bigDecimal3.equals(bigDecimal4));

        BigDecimal bigDecimal5 = new BigDecimal("1");
        BigDecimal bigDecimal6 = new BigDecimal("1.0");
        System.out.println(bigDecimal5.equals(bigDecimal6));

    }

We can find that when using the equals method of BigDecimal to compare 1 and 1.0: using int and double to define BigDecimal, the result is true; Using String to define BigDecimal, the result is false. Why does this happen?

Let's take a look at the source code of the equals method 👇

 /**
     * 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());
    }

In fact, we can find the answer from the annotation of the method: the equals method will compare two parts: value and scale. That is to say, although the values of bigDecimal5 and bigDecimal6 are the same, the scale is different.

Let's make a breakpoint and debug it~

We can see that the scale value of bigDecimal5 is 0, while the scale value of bigDecimal6 is 1, so the comparison result of bigDecimal5 and bigDecimal6 is false (●) ˇ ∀ ˇ ●)

Then another question arises: Why are the scales different? 🤔

Hee hee ~ take it easy, guys. Please listen to me~

BigDecimal has the following four construction methods:

  • BigDecimal(int)
  • BigDecimal(double)
  • BigDecimal(long)
  • BigDecimal(String)

The easiest to understand are BigDecimal(int) and BigDecimal(long). Because they are integers, the scale is 0 (the source code is as follows) 👇):

 /**
     * Translates an {@code int} into a {@code BigDecimal}.  The
     * scale of the {@code BigDecimal} is zero.
     *
     * @param val {@code int} value to be converted to
     *            {@code BigDecimal}.
     * @since  1.5
     */
    public BigDecimal(int val) {
        this.intCompact = val;
        this.scale = 0;
        this.intVal = null;
    }

``````
 /**
     * Translates a {@code long} into a {@code BigDecimal}.  The
     * scale of the {@code BigDecimal} is zero.
     *
     * @param val {@code long} value to be converted to {@code BigDecimal}.
     * @since  1.5
     */
    public BigDecimal(long val) {
        this.intCompact = val;
        this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
        this.scale = 0;
    }

For BigDecimal (double), when we use new BigDecimal (0.1) to create an object, the value of the created object is not equal to 0.1, but is equal to 0.100000000000055511123125782181583404541015625

Let's make another breakpoint and debug to see what the scale value is

We can see that the scale value is 55. How does this value come from? In fact, it is very simple. This scale value is the number of digits of this number, and the same is true for other floating-point numbers. For forms such as new BigDecimal (1.0) and new BigDecimal (1.00), because it is also an integer in essence, the scale of the number he creates is 0.

Finally, let's look at BigDecimal(String). When we use new BigDecimal ("0.1") to create a BigDecimal, the created value is exactly equal to 0.1. Then his scale is 1; If you use new BigDecimal ("0.10000"), the number created is 0.10000, and the scale is 5.

At this point, I believe you can understand why bigDecimal5 and bigDecimal6 use the equals method to make equivalence comparison, and the result is false O(∩ \ ∩) O

If we only want to judge whether two BigDecimal values are equal, how to judge?

BigDecimal also provides us with a method - compareTo method, which can only compare the values of two numbers. If the two numbers are equal, it will return 0.

After replacing equals with compareTo, we can find that the result of equivalent comparison between bigDecimal5 and bigDecimal6 is 0, which means that the values of the two are equal.

P.S. therefore, we should not casually use BigDecimal's equals method when making equivalence comparison. If we just want to compare the values, we should decisively choose the compareTo method~

Summary

My experience is limited, and some places may not be particularly good. If you have any questions when reading, please leave a message in the comment area, and we will discuss it later 🙇 ‍

If this article is helpful to you, please don't save your praise, thank you!

Topics: Java