How to correctly floor doubles in Java to two digits

Jos de Jong :

I'm trying to floor (not round!) doubles to two digits in a Java application. I use DecimalFormat for this, but noticed that for negative values close to zero, the values are not rounded to -0.01 but to -0.00.

public class MyTest {

    void formatAndPrint(double value) {
        DecimalFormat df = new DecimalFormat("0.00");
        df.setRoundingMode(RoundingMode.FLOOR);

        System.out.println(value + " => " + df.format(value));
    }

    @Test
    public void testFloor() {
        formatAndPrint(2.3289);  //  2.32
        formatAndPrint(2.3);     //  2.30
        formatAndPrint(-1.172);  // -1.18
        formatAndPrint(0.001);   //  0.00
        formatAndPrint(0.0001);  //  0.00
        formatAndPrint(-0.001);  // -0.01
        formatAndPrint(-0.0001); // -0.00 WRONG, expected: -0.01
    }
}

Other solutions like Math.floor(value * 100.0) / 100.0 does not have this issue but has other issues, like wrongly flooring 2.3 to 2.29.

Is there a flooring solution for Java which works in all cases?

Link64 :

The BigDecimal implementation works fine.

BigDecimal value = new BigDecimal(-0.0001);
value = value.setScale(2, BigDecimal.ROUND_FLOOR);
System.out.println(value.toPlainString());

Output: -0.01

2.3 will still floor to 2.29. This is not a result of the flooring, but instead the result of writing 2.3 as double. The closest 64-Bit IEEE754 representation of 2.3 is 2.29999...

When instantiating the BigDecimal you can instead provide a String like new BigDecimal("2.3") which will not result in such errors.


EDIT: As @Hulk mentioned, you can/should use BigDecimal.valueOf() instead, which will call Double.toString() for instantiation. Double.toString() does not use the exact value of the double but will instead "round" to the closest unique value.

Double JavaDoc:

There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double.

Here is a small table of long values (used with Double.longBitsToDouble(long) and their BigDecimal and Double.toString() counterparts:

long value            Double.toString()    new BigDecimal()
4612361558371493473 - 2.2999999999999976 - 2.299999999999997601918266809661872684955596923828125
4612361558371493474 - 2.299999999999998  - 2.29999999999999804600747665972448885440826416015625
4612361558371493475 - 2.2999999999999985 - 2.299999999999998490096686509787105023860931396484375
4612361558371493476 - 2.299999999999999  - 2.2999999999999989341858963598497211933135986328125
4612361558371493477 - 2.2999999999999994 - 2.299999999999999378275106209912337362766265869140625
4612361558371493478 - 2.3                - 2.29999999999999982236431605997495353221893310546875
4612361558371493479 - 2.3000000000000003 - 2.300000000000000266453525910037569701671600341796875
4612361558371493480 - 2.3000000000000007 - 2.300000000000000710542735760100185871124267578125
4612361558371493481 - 2.300000000000001  - 2.300000000000001154631945610162802040576934814453125
4612361558371493482 - 2.3000000000000016 - 2.30000000000000159872115546022541821002960205078125

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=474614&siteId=1