0

I want to get a BigDecimal value with the following attributes:

  • Rounding mode: Halfe-even
  • number of digits after the point: 2

I have the following code:

public BigDecimal standardDeviation() {
            MathContext mc = new MathContext (4,RoundingMode.HALF_EVEN);
            return new BigDecimal(Math.sqrt(variance().doubleValue()), mc);
        }

On test when i send some values, I got the following failure:

invalid standard deviation ==> expected: <16.73> but was: <16.72>

How can I solvw this?

Thomas
  • 87,414
  • 12
  • 119
  • 157
reema Abd
  • 1
  • 1
  • 3
    What would be the input value here? This seems to be just the wrong rounding mode selected or the wrong expectations. Note that HALF_EVEN means that `16.725` will be rounded to `16.72` instead of `16.73`, i.e. to the even neighbor. Did you mean to use HALF_UP instead? – Thomas Jul 07 '21 at 14:10
  • 1
    For several examples, you might want to show the value before rounding, after rounding, and what is expected. Otherwise a proper rounding mode can't be suggested to meet your requirements. And the term `exact`, imo, has little meaning when working with floating point values. – WJS Jul 07 '21 at 14:27
  • @Thomas, In other words: How can I convert 95 (BigDecimal) to 95.00 ? – reema Abd Jul 07 '21 at 14:36
  • That looks like a formatting issue. `BigDecimal b = new BigDecimal(95); System.out.printf("%.2f%n", b);` – WJS Jul 07 '21 at 14:42
  • @WJS Thank you! But can You tell me how to apply it on this function: public BigDecimal max() { BigDecimal max = Collections.max(dataToBeNormalized); return max ; } I need it to return 95.00 instead of 95 – reema Abd Jul 07 '21 at 14:47
  • 3
    Note that `95.00` and `95` is the exact same _value_ so if you're going to do calculations it just doesn't matter. Note that comparing `BigDecimals` should not be done using `equals()` but using `compareTo()` since equals will report 95 and 95.0 to be different (which they are but not from a math point of view). – Thomas Jul 07 '21 at 14:55
  • @Thomas I am working on a payment system, I need to return the numbers with the 2 digits after the point even if the number is like 95 but I want to return it as 95.00 That's what I need to apply on BigDecimal value – reema Abd Jul 07 '21 at 15:24
  • 3
    You're confusing appearance with the actual value. It's similar to an `int` of 0 and you want to return it as 00 or 000 or 0000. It doesn't make any difference. It is a formatting issue. You could return it as a String but then it would have to be converted back to a `BigDecimal` to use in computations. But depending on the value, you could loose valuable precision that could adversely affect those computations. – WJS Jul 07 '21 at 15:29
  • 1
    Perhaps someone cares to pull together all these good comments into an answer? – Basil Bourque Jul 07 '21 at 21:55

1 Answers1

0

Summarizing the comments:

invalid standard deviation ==> expected: <16.73> but was: <16.72>

Note that you very likely have a misconception on the rounding mode here.

Note that JavaDoc on HALF_EVEN:

"Behaves as for RoundingMode.HALF_UP if the digit to the left of thediscarded fraction is odd; behaves as for RoundingMode.HALF_DOWN if it's even"

That means a number like 16.725 would be rounded down rather than up so with HALF_EVEN the result will be 16.72.

How can I convert 95 (BigDecimal) to 95.00 ?

From a numeric perspective there is no difference between 95 and 95.00 since the trailing zeros are as insignificant as leading zeros are. In that sense even 0095.0000 would be the same numeric value.

When you're dealing with BigDecimal your first goal would normally be to get calculations and thus numerical values that don't suffer from "arbitrary" precision issues (indefinite precision is not supported but it can be set to a fixed value) and are as accurate as possible.

Hence if you create a BigDecimal from the numbers 95, 95.0 or even the string "95.00" they wil have the same value though.

However, they are not equal since internally the numbers also have a scale, i.e. 95 and 95.0 would normally be represented internally as the integer 95 and a scale of 0, which means the decimal point would not have to be moved. On the other hand "95.00" would be represented as the number 9500 and a scale of 2 which means to get the actual number you'd need to move the decimal point by 2 digits to the left.

That being said, if you want to check BigDecimals for equality don't use equals() as this also considers the scale but use compareTo() == 0 since that will compare the numerical value only.

I am working on a payment system, I need to return the numbers with the 2 digits after the point even if the number is like 95.

In general this is more of a formatting problem. Users of your system don't care how many fractional digits there are internally as long as calculations are done correctly. When you need to show a number to the user you need to convert it to a string and that's where formatting kicks in.

To generate a nicely formatted String you could use any of the methods based on java.utilFormatter e.g. String.format(printf("%.2f", bigDecimal);

Finally, there is a way to actually change the output of BigDecimal.toString() to 95.00 but I'd not recommend using it for that purpose: try new BigDecimal(95).setScale(2).toString().

Instead, format the output to the number of fraction digits you need and use methods like setScale() only to round correctly, e.g. if you want to round to 2 fraction digits try new BigDecimal(16.725).setScale(2, RoundingMode.HALF_UP)

Thomas
  • 87,414
  • 12
  • 119
  • 157