0

I would like to convert an Double to an Float with two decimal places.

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

class Scratch {
    private static final DecimalFormat df = new DecimalFormat("0.00");
    
    public static void main(String[] args) {
        Double worksAsIWouldExpect = 262143.05;
        Double worksNotAsIWouldExpect = 262144.05;

        convertAndPrint(worksAsIWouldExpect);
        //DecimalFormat: 262143.05
        //Big Decimal: 262143.05
        convertAndPrint(worksNotAsIWouldExpect);
        //DecimalFormat: 262144.06
        //Big Decimal: 262144.06
    }

    public static void convertAndPrint(Double doubleValue) {
        //DecimalFormat
        String stringValueDf = df.format(doubleValue);
        float floatValue = Float.parseFloat(stringValueDf);
        System.out.println("DecimalFormat: " + floatValue);

        //Big Decimal
        BigDecimal decimalValue = new BigDecimal(doubleValue.toString());
        decimalValue = decimalValue.setScale(2, RoundingMode.HALF_DOWN);
        floatValue = decimalValue.floatValue();
        System.out.println("Big Decimal: " + floatValue);
    }
}

Output:

DecimalFormat: 262143.05
Big Decimal: 262143.05
DecimalFormat: 262144.06
Big Decimal: 262144.06

I have the problem that when converting the values with the "DecimalFormat" as well as with the "BigDecimal" -approach, i get sometimes different values (or at least ones i would not excpect)

Why is 262144.05 (Double) converting to 262144.06 (Float) and not to 262144.05 (Float)?

  • Please add the output of your program. – aled May 24 '23 at 12:17
  • 1
    Use `BigDecimal.valueOf(double)`, that said, there is no guarantee whatsoever that you will get a float with 2 digits, because double and float are binary floating point types, and they cannot exactly represent all decimal numbers. – Mark Rotteveel May 24 '23 at 12:24
  • Be aware that using anything other than `String` to initialize a `BigDecimal` is pointless – g00se May 24 '23 at 12:24
  • Floating-point representations are inexact for substantially all real numbers within their ranges (though there are exceptions, some of them important). Type `float` has only about 6-7 decimal digits of precision; `double` about 15. – John Bollinger May 24 '23 at 12:25
  • @g00se Not pointless, but fraught with all kinds of nice (ahum) caveats. The existence of `new BigDecimal(double)` vs `BigDecimal.valueOf(double)` with different behaviours is also a great source of trouble. – Mark Rotteveel May 24 '23 at 12:26
  • Yes, I had to post that rapidly. Not pointless but ... almost. Always init with strings unless there's a very good reason not to – g00se May 24 '23 at 12:31

1 Answers1

1

Because you are introducing floating point representation errors by converting to float without need. Note that all floating point types (ie float and double) have representation errors. If you want to keep more precise don't convert to float and if possible avoid double too. You can skip some of the transformations by converting doubleValue directly to BigDecimal and printing the BigDecimal too.

BigDecimal decimalValue = new BigDecimal.valueOf(doubleValue);
decimalValue = decimalValue.setScale(2, RoundingMode.HALF_DOWN);
System.out.println("Big Decimal: " + decimalValue);

You can also use DecimalFormat with the BigDecimal directly.

aled
  • 21,330
  • 3
  • 27
  • 34
  • 1
    I would recommend using `BigDecimal.value(double)` instead of `new BigDecimal(double)`, as the behaviour of `valueOf` is more likely what people expect. – Mark Rotteveel May 24 '23 at 12:29
  • Thank you for the article! I thought that i need an Float object (i saw that the correct value was in the bigDecimal variable while debugging). Anyway i thought that i need an Float Object because iam saving the value in an Entity and then in an DB2-DB. But i just checked, its propably not correct in the first place to choose an Float object as an DECIMAL representation in the entity object. – Stefan Greil May 24 '23 at 12:55
  • @MarkRotteveel thanks for pointing that out. I updated my answer. For reference this answer explain the difference: https://stackoverflow.com/questions/7186204/bigdecimal-to-use-new-or-valueof – aled May 24 '23 at 14:52
  • 1
    @StefanGreil if an answer is useful feel free to accept&upvote. float is not a good idea because it has so low precision. double is a bit better but it still has representation errors as any floating point type. Specially if you are doing currency operation you will want to use BigDecimal to avoid errors in cents appearing from apparently nowhere. – aled May 24 '23 at 14:54
  • 1
    @StefanGreil If you're storing into a `DECIMAL` column, you should use `BigDecimal`. That is the standard mapping defined by JDBC for those types of columns. – Mark Rotteveel May 24 '23 at 16:25
  • @aled thank you, i upvoted the answer. Yes we will adapted that entity and dont use float anymore. – Stefan Greil May 24 '23 at 17:34
  • @Mark thank you, in the DB2 Docs also `BigDecimal` is mentioned for `DECIMAL` https://www.ibm.com/docs/en/db2-for-zos/12?topic=jsri-data-types-that-map-database-data-types-in-java-applications – Stefan Greil May 24 '23 at 17:35