I'm trying to round up a Double number whenever there is a decimal value. I'm trying the below. The below snippet is rounding up all other values as expected.
1.08 -> 2
9.5 -> 10
but rounds 0.08 as 0 instead of 1. Am I missing something here?
DecimalFormat df = new DecimalFormat("0");
df.setRoundingMode(RoundingMode.UP);
Double number = 0.08;
System.out.println(df.format(number)); // expecting 1 but produces 0
number = 1.08;
System.out.println(df.format(number)); // correctly produces 2
DecimalFormat(“0.00”) We can use DecimalFormat("0.00") to ensure the number always round to 2 decimal places. For DecimalFormat , the default rounding mode is RoundingMode. HALF_EVEN , and we can use setRoundingMode(RoundingMode) to set a specified rounding mode.
IEEE 754 defines four possible rounding modes: Round to nearest. This is the default mode.
public enum RoundingMode extends Enum<RoundingMode> Specifies a rounding behavior for numerical operations capable of discarding precision. Each rounding mode indicates how the least significant returned digit of a rounded result is to be calculated.
The default rounding mode of DecimalFormat is RoundingMode. HALF_EVEN . This means that it rounds up, or rounds down if the number is nearer to the next neighbour. When the number is exactly between two neighbours (in your case, 2 and 3), it rounds to the nearest even number (in your case, 2).
This looks like a manifestation of bug JDK-8174722, reported for JDK8, and still open with a fix version "tbd" (I see the same behavior on JDK14). In that bug report, rounding up 0.0001
to two fewer decimal places incorrectly results in 0.00
rather than 0.01
.
I tried to trace this calculation through the source code of DecimalFormat
and found multiple assumptions on numbers of significant digits, retained for compatibility but apparently the cause of issues like this. My hunch based on the source code is that if the first digit after the requested precision is 0 (or close enough) the function rounds down. This behavior is contrary to the documentation which clearly states otherwise.
If it were easy to fix, I gather the linked bug would have been fixed long before now, but it also appears to be a low priority.
As the comments have suggested, using Math.ceil()
, Math.floor()
, and Math.round()
functions would provide more predictable/consistent results and suffice for most use cases.
In your comments, you suggested the user provided the RoundingMode
, so if you want a direct application of that enum, the bug report's MCVE includes a workaround using BigDecimal.setScale()
. @Andreas posted an application of this workaround in this comment to produce this string:
// Note: this ignores Locale
BigDecimal.valueOf(number).setScale(0, RoundingMode.UP).toPlainString();
Another option using a DecimalFormat
would allow changing Locale settings and would be:
df.format(BigDecimal.valueOf(number).setScale(0, RoundingMode.UP));
Note that since you would use the rounding up in setScale()
you do not need to also use it in the DecimalFormat
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With