Search code examples
bigintegerbigdecimaljava

How to count digits in BigDecimal?


I’m dealing with BigDecimal in Java and I need to make 2 check against BigDecimal fields in my DTO:

  1. Number of digits of full part (before point) < 15
  2. Total number of digits < 32 including scale (zeros after point)

What is the best way to implement it? I extremely don’t want toBigInteger().toString() and .toString()


Solution

  • I think this will work.

          BigDecimal d = new BigDecimal("921229392299229.2922929292920000");
          int fractionCount = d.scale();
          System.out.println(fractionCount);
          int wholeCount = (int) (Math.ceil(Math.log10(d.longValue())));
          System.out.println(wholeCount);
    

    I did some testing of the above method vs using indexOf and subtracting lengths of strings. The above seems to be signficantly faster if my testing methodology is reasonable. Here is how I tested it.

          Random r = new Random(29);
          int nRuns = 1_000_000;
          // create a list of 1 million BigDecimals 
    
          List<BigDecimal> testData = new ArrayList<>();
          for (int j = 0; j < nRuns; j++) {
    
             String wholePart = r.ints(r.nextInt(15) + 1, 0, 10).mapToObj(
                   String::valueOf).collect(Collectors.joining());
    
             String fractionalPart = r.ints(r.nextInt(31) + 1, 0, 10).mapToObj(
                   String::valueOf).collect(Collectors.joining());
    
             BigDecimal d = new BigDecimal(wholePart + "." + fractionalPart);
             testData.add(d);
          }
    
          long start = System.nanoTime();
          // Using math
          for (BigDecimal d : testData) {
             int fractionCount = d.scale();
             int wholeCount = (int) (Math.ceil(Math.log10(d.longValue())));
          }
    
          long time = System.nanoTime() - start;
          System.out.println(time / 1_000_000.);
    
          start = System.nanoTime();
          //Using strings
          for (BigDecimal d : testData) {
             String sd = d.toPlainString();
             int n = sd.indexOf(".");
             int m = sd.length() - n - 1;
          }
    
          time = System.nanoTime() - start;
          System.out.println(time / 1_000_000.);
       }