Detailed explanation of Java BigDecimal

1 Introduction

        To borrow the words from the book Effactive Java, the main design goals of the float and double types are for scientific and engineering computing. They perform binary floating-point arithmetic, which is carefully designed to provide fast approximations with high accuracy over a wide range of numerical values. However, they do not provide completely accurate results and should not be used where exact results are required. However, business calculations often require accurate results, and this is where BigDecimal comes in handy.

 

2. Introduction to BigDecimal

       A BigDecimal consists of an arbitrary -precision integer unscaled value and a 32-bit integer scale . If zero or positive, the scale is the number of digits after the decimal point. If negative, the unscaled value of the number is multiplied by 10 to the negative scale power. Therefore, the value represented by BigDecimal is (unscaledValue × 10 -scale ) .

 

3. Test the code

3.1 Constructor (two common constructors whose main test parameter types are double and String)

       BigDecimal aDouble =new BigDecimal(1.22);

        System.out.println("construct with a double value: " + aDouble);

        BigDecimal aString =new BigDecimal("1.22");

         System.out.println("construct with a String value: " + aString);

        What do you think the output will be? If you didn't think the first one would output 1.22 , then congratulations you got it right, the output is as follows:

         construct with a doublevalue:1.2199999999999999733546474089962430298328399658203125

         construct with a String value: 1.22

        JDK description: 1. The result of the constructor whose parameter type is double is unpredictable. One might think that writing newBigDecimal(0.1) in Java creates a BigDecimal that is exactly equal to 0.1 (unscaled value 1, which has a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or in this case, as any finite-length binary fraction). Thus, the value passed to the constructor will not be exactly 0.1 (although ostensibly equal to that value).

        2. String constructors, on the other hand, are completely predictable: writing newBigDecimal("0.1") will create a BigDecimal that is exactly equal to the expected 0.1. Therefore, in comparison, it is generally recommended to use the String constructor in preference .

        3. When a double must be used as the source of a BigDecimal , note that this constructor provides an exact conversion; it does not provide the same result as using the method, then the constructor, to convert the double to String . To get that result, use the static  method.Double.toString(double)BigDecimal(String)valueOf(double)

3.2 Addition operation

        BigDecimal a =new BigDecimal("1.22");

        System.out.println("construct with a String value: " + a);

        BigDecimal b =new BigDecimal("2.22");

        a.add(b);

        System.out.println("aplus b is : " + a);

        It's easy to think that this would output:

        construct with a Stringvalue: 1.22

        a plus b is :3.44

        But actually a plus b is : 1.22

4. Source code analysis

4.1 valueOf( double val) method

    public   static BigDecimal valueOf(double val) {

       // Reminder: a zero double returns '0.0', so we cannotfastpath

       // to use the constant ZERO. This might be important enough to

       // justify a factory approach, a cache, or a few private

       // constants, later.

       return new BigDecimal( Double.toString (val)); // See the third point in 3.1 about the JDK description

    }

4.2 add(BigDecimal augend)方法

      public BigDecimal   add(BigDecimal augend) {

          long xs = this . intCompact ; // BigDecimal represented by integer numbers , for example , the intCompact value of a is 122

          long ys = augend.intCompact ; // same as above

          BigInteger fst = ( this . intCompact != INFLATED ) ? null : this . intVal ; // Initialize the value of BigInteger , intVal is a BigInteger property of BigDecimal

          BigInteger snd =(augend.intCompact !=INFLATED) ?null : augend.intVal;

          int rscale = this . scale ; // decimal places

 

          long sdiff = ( long )rscale - augend.scale ; // difference between decimal places

          if (sdiff != 0) { // take the number of decimal places as the number of decimal places of the result

              if (sdiff < 0) {

                 int raise =checkScale(-sdiff);

                 rscale =augend.scale;

                 if (xs ==INFLATED ||

                     (xs =longMultiplyPowerTen(xs,raise)) ==INFLATED)

                     fst =bigMultiplyPowerTen(raise);

                }else {

                   int raise =augend.checkScale(sdiff);

                   if (ys ==INFLATED ||(ys =longMultiplyPowerTen(ys,raise)) ==INFLATED)

                       snd = augend.bigMultiplyPowerTen(raise);

               }

          }

          if (xs !=INFLATED && ys !=INFLATED) {

              long sum = xs + ys;

              if ( (((sum ^ xs) &(sum ^ ys))) >= 0L) // Determine whether there is overflow

                 return BigDecimal.valueOf (sum,rscale); // Return the BigDecimal instance obtained using BigDecimal 's static factory method

           }

           if (fst ==null)

               fst =BigInteger.valueOf ( xs); // Static factory method of BigInteger

           if (snd ==null)

               snd =BigInteger.valueOf(ys);

           BigInteger sum =fst.add(snd);

           return (fst.signum == snd.signum) ?new BigDecimal(sum,INFLATED, rscale, 0) :

              new BigDecimal(sum, compactValFor (sum),rscale, 0); // Return the BigDecimal object obtained by other construction methods

       }

 

        The above is just an analysis of the addition source code. In fact, subtraction, multiplication and division ultimately returns a new BigDecimal object, because both BigInteger and BigDecimal are immutable ( immutable ), and a new object will be generated during each step of the operation. So a.add(b); Although the addition operation is performed, a does not save the value after the addition operation. The correct usage should be a=a.add(b);

 

5. Summary

        (1) Business calculations use BigDecimal.

        (2) Try to use the constructor whose parameter type is String.

        (3) BigDecimal is immutable ( immutable ), and a new object will be generated when performing each step of the operation, so be sure to save the value after the operation when doing addition, subtraction, multiplication and division.

        (4) We tend to ignore some implementation details at the bottom of the JDK, which leads to errors and requires more attention.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326003497&siteId=291194637