Forum Stats

  • 3,826,768 Users
  • 2,260,705 Discussions
  • 7,897,072 Comments

Discussions

Rounding behavior JDK 8 vs. JDK 7

Karl Eilebrecht
Karl Eilebrecht Member Posts: 2
edited Dec 13, 2015 3:55AM in Java 8 Questions

UPDATE / RESOLVED

In the meantime I have found a good explanation given by the guys at Open JDK: https://bugs.openjdk.java.net/browse/JDK-8029896

The problem is indeed related to IEEE 754 number representation. Older JDKs like JDK 7 incorrectly implemented rounding double value representations close to ties (like 0.005).

All the <input> cases I listed earlier can be explained by adding System.outprintln((new BigDecimal(<input>)).toString()).

As you can see none of them can be represented exactly.

Here are some examples:

                                       

inputclosest representationJDK7 round HALF_EVENJDK8 round HALF_EVEN
0.0050.0050000000000000001040834085586084256647154688835144042968750.000.01
0.0150.014999999999999999444888487687421729788184165954589843750.020.01
0.0250.0250000000000000013877787807814456755295395851135253906250.020.03
0.0650.0650000000000000022204460492503130808472633361816406250.060.07
0.0750.074999999999999997224442438437108648940920829772949218750.080.07
0.0850.085000000000000006106226635438360972329974174499511718750.080.09
0.1550.15499999999999999888977697537484345957636833190917968750.160.15
0.1650.16500000000000000777156117237609578296542167663574218750.160.17
0.1750.1749999999999999888977697537484345957636833190917968750.180.17

If you now apply rounding with RoundingMode.HALF_EVEN it turns out that JDK7-behavior is wrong while JDK8 behavior is correct. This is the problem with IEEE 754, not with Java. Programs that now show different (unacceptable) behavior compared to JDK7 should be adjusted to using BigDecimal.

The change affects > 4% of all rounding operations from n decimals to n-1 decimals with n > 1.

------------------------ original post ---------------------------------------

I have read some articles about the improved rounding behavior of the NumberFormat class in JDK 8. This was for example discussed here: http://stackoverflow.com/questions/22797964/is-inconsistency-in-rounding-between-java-7-and-java-8-a-bug

As far as I understand, the new behavior shall better align to the well-known problems with IEEE 754 (https://en.wikipedia.org/wiki/IEEE_floating_point) regarding number representation (eliminate some errorneous corner cases). This should be a minor change, but it isn't somehow (see below).

I ran the code given below with JDK 7 (1.7.0_10) and JDK 8 (1.8.0_65) on Mac OS.

It simply compares rounding 1 decimal first performed on a regular double and then on the corresponding BigDecimal. The default RoundingMode is HALF_EVEN.

Surprising to me is that compared to JDK 7 the number of cases where the rounding mechanism of JDK 8 deviates from the JDK 7 behavior is much higher than I expected.

For JDK 7 all output is 0% (means same result for BigDecimal double). For JDK 8 results look different:

Range [0.0..100000.0), step=0.1 -> diff=0.00%, diffAtTie=0.00%

Range [0.00..10000.00), step=0.01 -> diff=4.00%, diffAtTie=4.00%

Range [0.000..1000.000), step=0.001 -> diff=4.80%, diffAtTie=4.80%

Range [0.0000..100.0000), step=0.0001 -> diff=4.96%, diffAtTie=4.96%

Range [0.00000..10.00000), step=0.00001 -> diff=4.99%, diffAtTie=4.99%

Range [0.000000..1.000000), step=0.000001 -> diff=5.00%, diffAtTie=5.00%

Range [0.0..-100000.0), step=-0.1 -> diff=0.00%, diffAtTie=0.00%

Range [0.00..-10000.00), step=-0.01 -> diff=4.00%, diffAtTie=4.00%

Range [0.000..-1000.000), step=-0.001 -> diff=4.80%, diffAtTie=4.80%

Range [0.0000..-100.0000), step=-0.0001 -> diff=4.96%, diffAtTie=4.96%

Range [0.00000..-10.00000), step=-0.00001 -> diff=4.99%, diffAtTie=4.99%

Range [0.000000..-1.000000), step=-0.000001 -> diff=5.00%, diffAtTie=5.00%

Here my questions:

  • Has this non-trivial change been announced anywhere (maybe explained in detail)?
  • Is this also surprising to you or am I on the wrong page?
  • Can anybody explain the error distribution phenomenon? Starting with the second decimal suddenly the error ratio grows from 4% up to 5 % - and it is always the "HALF" (tie) which gets rounded to the "wrong" side.

Thanks!

Karl

EDIT: As requested here some example values JDK7 (1.7.0_10) vs. JDK8 (1.8.0_65) on Mac OS El Capitan 10.11.2.

                                                                                                                                                                                                                                                                                                                                                                                                                                                             

For generating the small sample below I used a simpler generated source code, which can be found at the bottom of the table.

The sample only shows rounding from 3 to 2 decimals, but the phenomenon can be seen on any rounding from n to n-1 decimals with n > 1.

As you can see the values in the red columns are different compared to JDK7. This is what makes me nervous (round about 5%).

The BigDecimal columns are just for information. The difference between passing a double vs. passing a String to BigDecimal is plausible (precision loss).

                                                                                                                                                                                                                                                                                                                                                                                                                                                              

                                                                                                                                                                                                                                                                                                                                                                                                                                                               

inputJDK7 nf.format(
input)
JDK7   nf.format(
Double.
parseDoub
le(
"input"))
JDK7 nf.format(
new   BigDecimal(
input))
JDK7 nf.format(
new   BigDecimal(
"input"))
JDK8 nf.format(
input)
JDK8   nf.format(
Double.
parseDoub
le("input"))
JDK8 nf.format(
new   BigDecimal(
input))
JDK8 nf.format(
new   BigDecimal(
"input"))
0.0050.000.000.010.000.010.010.010.00
0.0150.020.020.010.020.010.010.010.02
0.0250.020.020.030.020.030.030.030.02
0.0650.060.060.070.060.070.070.070.06
0.0750.080.080.070.080.070.070.070.08
0.0850.080.080.090.080.090.090.090.08
0.1550.160.160.150.160.150.150.150.16
0.1650.160.160.170.160.170.170.170.16
0.1750.180.180.170.180.170.170.170.18
0.2150.220.220.210.220.210.210.210.22
0.2250.220.220.230.220.230.230.230.22
0.2350.240.240.230.240.230.230.230.24
0.2650.260.260.270.260.270.270.270.26
0.2950.300.300.290.300.290.290.290.30
0.3250.320.320.330.320.330.330.330.32
0.3550.360.360.350.360.350.350.350.36
0.3850.380.380.390.380.390.390.390.38
0.4050.400.400.410.400.410.410.410.40
0.4150.420.420.410.420.410.410.410.42
0.4350.440.440.430.440.430.430.430.44
0.4450.440.440.450.440.450.450.450.44
0.4650.460.460.470.460.470.470.470.46
0.4750.480.480.470.480.470.470.470.48
0.4950.500.500.490.500.490.490.490.50
0.5050.500.500.510.500.510.510.510.50
0.5250.520.520.530.520.530.530.530.52
0.5450.540.540.550.540.550.550.550.54
0.5750.580.580.570.580.570.570.570.58
0.5950.600.600.590.600.590.590.590.60
0.6150.620.620.610.620.610.610.610.62
0.6450.640.640.650.640.650.650.650.64
0.6650.660.660.670.660.670.670.670.66
0.6850.680.680.690.680.690.690.690.68
0.6950.700.700.690.700.690.690.690.70
0.7150.720.720.710.720.710.710.710.72
0.7350.740.740.730.740.730.730.730.74
0.7650.760.760.770.760.770.770.770.76
0.7850.780.780.790.780.790.790.790.78
0.8050.800.800.810.800.810.810.810.80
0.8150.820.820.810.820.810.810.810.82
0.8350.840.840.830.840.830.830.830.84
0.8550.860.860.850.860.850.850.850.86
0.8850.880.880.890.880.890.890.890.88
0.9050.900.900.910.900.910.910.910.90
0.9250.920.920.930.920.930.930.930.92
0.9550.960.960.950.960.950.950.950.96
0.9750.980.980.970.980.970.970.970.98
0.9951.001.000.991.000.990.990.991.00
1.0151.021.021.011.021.011.011.011.02

NumberFormat nf = NumberFormat.getInstance(Locale.US);
assertEquals(RoundingMode.HALF_EVEN, nf.getRoundingMode());
nf.setGroupingUsed(false);
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(2);
System.out.println("0.005;" + nf.format(0.005) + ";" + nf.format(Double.parseDouble("0.005")) + ";"
+ nf.format(new BigDecimal(0.005)) + ";" + nf.format(new BigDecimal("0.005")));
System.out.println("0.015;" + nf.format(0.015) + ";" + nf.format(Double.parseDouble("0.015")) + ";"
+ nf.format(new BigDecimal(0.015)) + ";" + nf.format(new BigDecimal("0.015")));
System.out.println("0.025;" + nf.format(0.025) + ";" + nf.format(Double.parseDouble("0.025")) + ";"
+ nf.format(new BigDecimal(0.025)) + ";" + nf.format(new BigDecimal("0.025")));
System.out.println("0.065;" + nf.format(0.065) + ";" + nf.format(Double.parseDouble("0.065")) + ";"
+ nf.format(new BigDecimal(0.065)) + ";" + nf.format(new BigDecimal("0.065")));
System.out.println("0.075;" + nf.format(0.075) + ";" + nf.format(Double.parseDouble("0.075")) + ";"
+ nf.format(new BigDecimal(0.075)) + ";" + nf.format(new BigDecimal("0.075")));
System.out.println("0.085;" + nf.format(0.085) + ";" + nf.format(Double.parseDouble("0.085")) + ";"
+ nf.format(new BigDecimal(0.085)) + ";" + nf.format(new BigDecimal("0.085")));
System.out.println("0.155;" + nf.format(0.155) + ";" + nf.format(Double.parseDouble("0.155")) + ";"
+ nf.format(new BigDecimal(0.155)) + ";" + nf.format(new BigDecimal("0.155")));
System.out.println("0.165;" + nf.format(0.165) + ";" + nf.format(Double.parseDouble("0.165")) + ";"
+ nf.format(new BigDecimal(0.165)) + ";" + nf.format(new BigDecimal("0.165")));
System.out.println("0.175;" + nf.format(0.175) + ";" + nf.format(Double.parseDouble("0.175")) + ";"
+ nf.format(new BigDecimal(0.175)) + ";" + nf.format(new BigDecimal("0.175")));
System.out.println("0.215;" + nf.format(0.215) + ";" + nf.format(Double.parseDouble("0.215")) + ";"
+ nf.format(new BigDecimal(0.215)) + ";" + nf.format(new BigDecimal("0.215")));
System.out.println("0.225;" + nf.format(0.225) + ";" + nf.format(Double.parseDouble("0.225")) + ";"
+ nf.format(new BigDecimal(0.225)) + ";" + nf.format(new BigDecimal("0.225")));
System.out.println("0.235;" + nf.format(0.235) + ";" + nf.format(Double.parseDouble("0.235")) + ";"
+ nf.format(new BigDecimal(0.235)) + ";" + nf.format(new BigDecimal("0.235")));
System.out.println("0.265;" + nf.format(0.265) + ";" + nf.format(Double.parseDouble("0.265")) + ";"
+ nf.format(new BigDecimal(0.265)) + ";" + nf.format(new BigDecimal("0.265")));
System.out.println("0.295;" + nf.format(0.295) + ";" + nf.format(Double.parseDouble("0.295")) + ";"
+ nf.format(new BigDecimal(0.295)) + ";" + nf.format(new BigDecimal("0.295")));
System.out.println("0.325;" + nf.format(0.325) + ";" + nf.format(Double.parseDouble("0.325")) + ";"
+ nf.format(new BigDecimal(0.325)) + ";" + nf.format(new BigDecimal("0.325")));
System.out.println("0.355;" + nf.format(0.355) + ";" + nf.format(Double.parseDouble("0.355")) + ";"
+ nf.format(new BigDecimal(0.355)) + ";" + nf.format(new BigDecimal("0.355")));
System.out.println("0.385;" + nf.format(0.385) + ";" + nf.format(Double.parseDouble("0.385")) + ";"
+ nf.format(new BigDecimal(0.385)) + ";" + nf.format(new BigDecimal("0.385")));
System.out.println("0.405;" + nf.format(0.405) + ";" + nf.format(Double.parseDouble("0.405")) + ";"
+ nf.format(new BigDecimal(0.405)) + ";" + nf.format(new BigDecimal("0.405")));
System.out.println("0.415;" + nf.format(0.415) + ";" + nf.format(Double.parseDouble("0.415")) + ";"
+ nf.format(new BigDecimal(0.415)) + ";" + nf.format(new BigDecimal("0.415")));
System.out.println("0.435;" + nf.format(0.435) + ";" + nf.format(Double.parseDouble("0.435")) + ";"
+ nf.format(new BigDecimal(0.435)) + ";" + nf.format(new BigDecimal("0.435")));
System.out.println("0.445;" + nf.format(0.445) + ";" + nf.format(Double.parseDouble("0.445")) + ";"
+ nf.format(new BigDecimal(0.445)) + ";" + nf.format(new BigDecimal("0.445")));
System.out.println("0.465;" + nf.format(0.465) + ";" + nf.format(Double.parseDouble("0.465")) + ";"
+ nf.format(new BigDecimal(0.465)) + ";" + nf.format(new BigDecimal("0.465")));
System.out.println("0.475;" + nf.format(0.475) + ";" + nf.format(Double.parseDouble("0.475")) + ";"
+ nf.format(new BigDecimal(0.475)) + ";" + nf.format(new BigDecimal("0.475")));
System.out.println("0.495;" + nf.format(0.495) + ";" + nf.format(Double.parseDouble("0.495")) + ";"
+ nf.format(new BigDecimal(0.495)) + ";" + nf.format(new BigDecimal("0.495")));
System.out.println("0.505;" + nf.format(0.505) + ";" + nf.format(Double.parseDouble("0.505")) + ";"
+ nf.format(new BigDecimal(0.505)) + ";" + nf.format(new BigDecimal("0.505")));
System.out.println("0.525;" + nf.format(0.525) + ";" + nf.format(Double.parseDouble("0.525")) + ";"
+ nf.format(new BigDecimal(0.525)) + ";" + nf.format(new BigDecimal("0.525")));
System.out.println("0.545;" + nf.format(0.545) + ";" + nf.format(Double.parseDouble("0.545")) + ";"
+ nf.format(new BigDecimal(0.545)) + ";" + nf.format(new BigDecimal("0.545")));
System.out.println("0.575;" + nf.format(0.575) + ";" + nf.format(Double.parseDouble("0.575")) + ";"
+ nf.format(new BigDecimal(0.575)) + ";" + nf.format(new BigDecimal("0.575")));
System.out.println("0.595;" + nf.format(0.595) + ";" + nf.format(Double.parseDouble("0.595")) + ";"
+ nf.format(new BigDecimal(0.595)) + ";" + nf.format(new BigDecimal("0.595")));
System.out.println("0.615;" + nf.format(0.615) + ";" + nf.format(Double.parseDouble("0.615")) + ";"
+ nf.format(new BigDecimal(0.615)) + ";" + nf.format(new BigDecimal("0.615")));
System.out.println("0.645;" + nf.format(0.645) + ";" + nf.format(Double.parseDouble("0.645")) + ";"
+ nf.format(new BigDecimal(0.645)) + ";" + nf.format(new BigDecimal("0.645")));
System.out.println("0.665;" + nf.format(0.665) + ";" + nf.format(Double.parseDouble("0.665")) + ";"
+ nf.format(new BigDecimal(0.665)) + ";" + nf.format(new BigDecimal("0.665")));
System.out.println("0.685;" + nf.format(0.685) + ";" + nf.format(Double.parseDouble("0.685")) + ";"
+ nf.format(new BigDecimal(0.685)) + ";" + nf.format(new BigDecimal("0.685")));
System.out.println("0.695;" + nf.format(0.695) + ";" + nf.format(Double.parseDouble("0.695")) + ";"
+ nf.format(new BigDecimal(0.695)) + ";" + nf.format(new BigDecimal("0.695")));
System.out.println("0.715;" + nf.format(0.715) + ";" + nf.format(Double.parseDouble("0.715")) + ";"
+ nf.format(new BigDecimal(0.715)) + ";" + nf.format(new BigDecimal("0.715")));
System.out.println("0.735;" + nf.format(0.735) + ";" + nf.format(Double.parseDouble("0.735")) + ";"
+ nf.format(new BigDecimal(0.735)) + ";" + nf.format(new BigDecimal("0.735")));
System.out.println("0.765;" + nf.format(0.765) + ";" + nf.format(Double.parseDouble("0.765")) + ";"
+ nf.format(new BigDecimal(0.765)) + ";" + nf.format(new BigDecimal("0.765")));
System.out.println("0.785;" + nf.format(0.785) + ";" + nf.format(Double.parseDouble("0.785")) + ";"
+ nf.format(new BigDecimal(0.785)) + ";" + nf.format(new BigDecimal("0.785")));
System.out.println("0.805;" + nf.format(0.805) + ";" + nf.format(Double.parseDouble("0.805")) + ";"
+ nf.format(new BigDecimal(0.805)) + ";" + nf.format(new BigDecimal("0.805")));
System.out.println("0.815;" + nf.format(0.815) + ";" + nf.format(Double.parseDouble("0.815")) + ";"
+ nf.format(new BigDecimal(0.815)) + ";" + nf.format(new BigDecimal("0.815")));
System.out.println("0.835;" + nf.format(0.835) + ";" + nf.format(Double.parseDouble("0.835")) + ";"
+ nf.format(new BigDecimal(0.835)) + ";" + nf.format(new BigDecimal("0.835")));
System.out.println("0.855;" + nf.format(0.855) + ";" + nf.format(Double.parseDouble("0.855")) + ";"
+ nf.format(new BigDecimal(0.855)) + ";" + nf.format(new BigDecimal("0.855")));
System.out.println("0.885;" + nf.format(0.885) + ";" + nf.format(Double.parseDouble("0.885")) + ";"
+ nf.format(new BigDecimal(0.885)) + ";" + nf.format(new BigDecimal("0.885")));
System.out.println("0.905;" + nf.format(0.905) + ";" + nf.format(Double.parseDouble("0.905")) + ";"
+ nf.format(new BigDecimal(0.905)) + ";" + nf.format(new BigDecimal("0.905")));
System.out.println("0.925;" + nf.format(0.925) + ";" + nf.format(Double.parseDouble("0.925")) + ";"
+ nf.format(new BigDecimal(0.925)) + ";" + nf.format(new BigDecimal("0.925")));
System.out.println("0.955;" + nf.format(0.955) + ";" + nf.format(Double.parseDouble("0.955")) + ";"
+ nf.format(new BigDecimal(0.955)) + ";" + nf.format(new BigDecimal("0.955")));
System.out.println("0.975;" + nf.format(0.975) + ";" + nf.format(Double.parseDouble("0.975")) + ";"
+ nf.format(new BigDecimal(0.975)) + ";" + nf.format(new BigDecimal("0.975")));
System.out.println("0.995;" + nf.format(0.995) + ";" + nf.format(Double.parseDouble("0.995")) + ";"
+ nf.format(new BigDecimal(0.995)) + ";" + nf.format(new BigDecimal("0.995")));
System.out.println("1.015;" + nf.format(1.015) + ";" + nf.format(Double.parseDouble("1.015")) + ";"
+ nf.format(new BigDecimal(1.015)) + ";" + nf.format(new BigDecimal("1.015")));



package comp7;

import static org.junit.Assert.assertEquals;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;

import org.junit.Test;

public class JavaRoundTest {

    @Test
    public void testJavaNumberFormatRounding() throws Exception {
        int total = 1_000_000;
        long time = System.currentTimeMillis();
        for (boolean negate : new boolean[] { false, true }) {
            for (int decimals = 1; decimals < 7; decimals++) {
                performRun(total, negate, decimals);
            }
        }
        System.out.println("Finished after " + (System.currentTimeMillis() - time) + " ms");

    }

    private void performRun(int upperBound, boolean negate, int decimals) {
        NumberFormat testedNumberFormat = NumberFormat.getInstance(Locale.US);
        assertEquals(RoundingMode.HALF_EVEN, testedNumberFormat.getRoundingMode());
        testedNumberFormat.setGroupingUsed(false);
        testedNumberFormat.setMaximumFractionDigits(decimals - 1);
        testedNumberFormat.setMinimumFractionDigits(decimals - 1);
        DiffCountResult dcr = computeRun(testedNumberFormat, upperBound, negate, decimals);
        double diffShare = ((double) dcr.diffCount / upperBound);
        double diffAtTieShare = ((double) dcr.diffAtTieCount / upperBound);
        NumberFormat percNumberFormat = NumberFormat.getPercentInstance(Locale.US);
        percNumberFormat.setMaximumFractionDigits(2);
        percNumberFormat.setMinimumFractionDigits(2);
        percNumberFormat.setGroupingUsed(true);

        System.out.println("Range [" + makeDecimalString(0, negate, decimals) + ".."
                + makeDecimalString(upperBound, negate, decimals) + "), step=" + makeDecimalString(1, negate, decimals)
                + " -> diff=" + percNumberFormat.format(new BigDecimal(diffShare)) + ", diffAtTie="
                + percNumberFormat.format(new BigDecimal(diffAtTieShare)));
    }

    private DiffCountResult computeRun(NumberFormat nf, int upperBound, boolean negate, int decimals) {
        DiffCountResult dcr = new DiffCountResult();
        for (int i = 0; i < upperBound; i++) {
            String sNum = makeDecimalString(i, negate, decimals);
            String sRoundedDouble = nf.format(Double.parseDouble(sNum));
            String sRoundedBigDecimal = nf.format(new BigDecimal(sNum));
            if (!sRoundedDouble.equals(sRoundedBigDecimal)) {
                dcr.diffCount++;
                if (sNum.charAt(sNum.length() - 1) == '5') {
                    dcr.diffAtTieCount++;
                }
            }
        }
        return dcr;
    }

    static String makeDecimalString(int sourceValue, boolean negate, int decimals) {
        String res = "0.";
        String sourceString = "" + sourceValue;
        int sourceLen = sourceString.length();
        if (sourceLen <= decimals) {
            int padLen = decimals - sourceLen;
            for (int i = 0; i < padLen; i++) {
                res = res + "0";
            }
            res = res + sourceString;
        } else {
            int dotPos = sourceLen - decimals;
            res = sourceString.substring(0, dotPos) + "." + sourceString.substring(dotPos);
        }
        if (sourceValue > 0 && negate) {
            res = "-" + res;
        }
        return res;
    }

    static class DiffCountResult {
        int diffCount = 0;
        int diffAtTieCount = 0;
    }

}

Answers

  • Unknown
    edited Dec 11, 2015 4:25PM
    Surprising to me is that compared to JDK 7 the number of cases where the rounding mechanism of JDK 8 deviates from the JDK 7 behavior is much higher than I expected.
    

    Ok - but we have NO WAY of knowing 1) what your 'expectations' were, 2) how you reached your 'expectations or 3) whether your expectations are valid.

     Here my questions:
    
    Has this non-trivial change been announced anywhere (maybe explained in detail)?
    Is this also surprising to you or am I on the wrong page?
    Can anybody explain the error distribution phenomenon? Starting with the second decimal suddenly the error ratio grows from 4% up to 5 % - and it is always the "HALF" (tie) which gets rounded to the "wrong" side.
    
    

    That forum thread you linked to included links to where the changes were announced and explained them in detail.

    If you think you have found a bug you need to:

    1. login to your MOS account

    2. search to see if the issue has already been reported

    3. if not open an SR and provide a SIMPLE test case

    For a rounding issue all you need is ONE example that you think is wrong. We can't really identify what you think is wrong based on what you posted. A simple test case is similar to this

    1. I have a number in an instance of this (provide the datatype) datatype

    2. This code in JDK 7 version (provide the FULL version and platform) rounds it with a result of xxxxx (provide the result)

    3. This code in JDK 8 version (full version/platform) rounds it differently with a result of xxxxx (provide the result).

    You also need to ensure that your results are NOT being affected by implicit conversions that are done from the raw datatype value to a string format that is used to display the value.

  • Karl Eilebrecht
    Karl Eilebrecht Member Posts: 2
    edited Dec 12, 2015 8:58AM

    Hi,

    Thank you for your quick reply! I'm sorry if my description was unclear.

    In fact this is a regression test 1.7.0_10 against 1.8.0_65 on Mac OS. My initial expectation was "same behavior with Java 8 as with Java 7".

    However, with Java 7 there are no differences between rounding a BigDecimal vs. rounding the corresponding double with my set of test data. For Java 8 I see up to 5% deviation. I still don't think it is necessarily a bug (might be intended behavior). I just hope to get more insights. If a "corner-case-bug" was fixed and now a lot of trivial cases show differences it would be great to read some background from guys behind the scenes (I admit that my knowledge about IEEE 754 and implementation details in the JVM is limited ).

    I have attached sample data to the original post.

    Regards.

    Karl