Search code examples
javaunits-of-measurement

How do I format a JSR-385 Quantity with fixed number of decimal digits?


I'm trying to convert hard-coded formatting to a Java Units API implementation.

The existing code outputs (for this example, the temperature value in degrees) with two decimal places. For example, 38.70°C. While I'd like to allow the user to specify their own formatting code (which is the end-goal of the change), I think it would be useful to keep the legacy behavior to give people a chance to migrate.

The existing code looks like:

        return String.format("%.2f\u00B0C", this.temperature);

The code I'm trying to use looks like:

        DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance();
        numberFormat.setMinimumFractionDigits(2);
        numberFormat.setMaximumFractionDigits(2);
        NumberDelimiterQuantityFormat formatter =
                NumberDelimiterQuantityFormat.builder()
                        .setNumberFormat(numberFormat)
                        .setDelimiter("")
                        .setUnitFormat(SimpleUnitFormat.getInstance())
                        .build();
        return formatter.format(temperature);

It does format, but not with the specified precision. I'd expect 38.70°C but instead get 38.70000076293945℃.

If I just do

numberFormat.format(temperature.getValue().floatValue());

then it does format correctly ("38.70"). So I think the DecimalFormat is basically OK.

I considered just manually building my formatting. However that doesn't really work for what I want to do - pass in the NumberDelimiterQuantityFormat (or applicable interface).

Can anyone suggest an appropriate way to format a Quantity<> with fixed decimal precision?


Solution

  • First off, I'm completely unfamiliar with the Java Unit API and this implementation, but this seemed like an interesting question, so I looked into it.

    I had a look at the implementation of NumberDelimiterQuantityFormat and right there in the implementation of the format method it modifies the maxiumFractionDigits of the NumberFormat depending on the fraction

    if (quantity != null && quantity.getValue() != null) {
        fract = getFractionDigitsCount(quantity.getValue().doubleValue());
    }
    if (fract > 1) {
        numberFormat.setMaximumFractionDigits(fract + 1);
    }
    

    Source

    This makes little sense to me for two reasons:

    1. It negates the whole reason to have a NumberFormat in the first place especially in context with floating point numbers where it's virtually impossible to avoid superfluous fraction digits.

    2. It modifies the internal state of the NumberDelimiterQuantityFormat in a method where it isn't expected.

    I should have checked first, but there is actually an issue about this, which is "being analyzed" for several months now. Maybe it would make sense to ask in there.