0

I am using BigDecimal for division. I would like the quotient to be rounded to the correct number of significant figures.

For example

  @Test
  public void testBigDDivision() {

    BigDecimal top = new BigDecimal("0.25");
    BigDecimal bottom = new BigDecimal("105");

    int topSigFig = significantDigits(top);
    int botSigFig = significantDigits(bottom);

    // evaluates to 2 in this example
    int scale = (topSigFig > botSigFig) ? botSigFig : topSigFig;

    BigDecimal quot = top.divide(bottom, scale, RoundingMode.HALF_UP);

    BigDecimal expected = new BigDecimal("0.0024");

    Assert.assertTrue(String.format("Got %s; Expected %s", quot, expected), 
      expected.compareTo(quot) == 0); // fails "Got 0.00; Expected 0.0024"
    }

    // stolen from https://stackoverflow.com/a/21443880
    public static int significantDigits(BigDecimal input) {
      input = input.stripTrailingZeros();
      return input.scale() < 0
        ? input.precision() - input.scale()
        : input.precision();
    }

What is the correct way to programmatically determine the scale to ensure the quotient has the correct number of significant figures?

Oliver Reimer
  • 15
  • 1
  • 4
  • *scale - scale of the BigDecimal quotient to be returned.* Is not `0.0024` to 2 digits == `0.00` ? – Scary Wombat Sep 20 '18 at 00:20
  • How about `BigDecimal quot = top.divide(bottom, RoundingMode.HALF_UP);` then scale? – Scary Wombat Sep 20 '18 at 00:20
  • @iajrz Yep, not claiming otherwise – Scary Wombat Sep 20 '18 at 00:30
  • I uttered a wrong comment, @ScaryWombat; got confused to what you were suggesting; the actual response is: if you divide & round without specifying scale, the scale is automatically calculated from the scales of the original numbers as specified here: https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html; this will not give him the amount of characters he wants to have. – iajrz Sep 20 '18 at 00:34
  • @Oliver Why do you want to do this? The concept of significant digits is context-dependent, not operation-dependent. If we knew why you're trying to do this we may be able to help. – iajrz Sep 20 '18 at 00:36
  • @iajrz The use case is a bit complex. To summarize, I have a solution of N ingredients and water. I know that ingredient A makes up 10% of the solution. If the solution is diluted (add more water), I am trying to recalculate the new percentage of the solution contains ingredient A. Due to rounding, I often end up with an inaccurate amount (as illustrated above). Maybe the solution is using a large fixed scale (e.g. 10). – Oliver Reimer Sep 20 '18 at 02:11

1 Answers1

0

Significant figures are situational, not computable. As you mentioned in the comment, you're doing a program to recalculate the percentage of a solution with several ingredients. I suggest you transform the ingredients' units until you have no significant digits to the right of the decimal point in the input, then do the calculation.

For this you need to know the unit in the input. So, if the test input is in "grams", you'd first transform to milligrams (grams*1000).

So numbers would be 250 and 105000; then do the division and keep 2 or 3 decimal digits - less than that doesn't usually make sense when the input has no decimal numbers.

iajrz
  • 749
  • 7
  • 16
  • Piling on: multiplying grams by 1000 to get milligrams will not increase significant digits. – nicomp Sep 20 '18 at 21:43
  • 1
    Correct - the idea is to reduce the number of digits after the point needed to have a meaningful answer. This is what I would do in order to not try to calculate the significant digits in a division - which I think is not trivially computable. – iajrz Sep 20 '18 at 21:44