Tracing through the code you get to DigitList.set
final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
FloatingDecimal.BinaryToASCIIConverter fdConverter = FloatingDecimal.getBinaryToASCIIConverter(source);
boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp();
I have a simpler test for this bug
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;
public class Test {
public static void main(String... args) {
for (int i = 0; i < 100; i++)
test(i / 100.0);
}
private static void test(double num) {
NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
nf.setMaximumFractionDigits(1);
String round1 = nf.format(num);
NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
nf2.setMaximumFractionDigits(1);
nf2.setRoundingMode(RoundingMode.HALF_UP);
String round2 = nf2.format(num);
if (!round1.equals(round2))
System.out.printf("%s, formatted with HALF_UP was %s but should be %s%n", num, round2, round1);
}
}
prints
0.06, formatted with HALF_UP was 0 but should be 0.1
0.09, formatted with HALF_UP was 0 but should be 0.1
0.18, formatted with HALF_UP was 0.1 but should be 0.2
0.25, formatted with HALF_UP was 0.3 but should be 0.2
0.29, formatted with HALF_UP was 0.2 but should be 0.3
0.36, formatted with HALF_UP was 0.3 but should be 0.4
0.37, formatted with HALF_UP was 0.3 but should be 0.4
0.47, formatted with HALF_UP was 0.4 but should be 0.5
0.48, formatted with HALF_UP was 0.4 but should be 0.5
0.49, formatted with HALF_UP was 0.4 but should be 0.5
0.57, formatted with HALF_UP was 0.5 but should be 0.6
0.58, formatted with HALF_UP was 0.5 but should be 0.6
0.59, formatted with HALF_UP was 0.5 but should be 0.6
0.69, formatted with HALF_UP was 0.6 but should be 0.7
0.86, formatted with HALF_UP was 0.8 but should be 0.9
0.87, formatted with HALF_UP was 0.8 but should be 0.9
0.96, formatted with HALF_UP was 0.9 but should be 1
0.97, formatted with HALF_UP was 0.9 but should be 1
0.98, formatted with HALF_UP was 0.9 but should be 1
0.99, formatted with HALF_UP was 0.9 but should be 1
In the incorrect case hasBeenRoundedUp
is true and this prevents any further rounding up. Note, if you drop setting the rounding, it has a default path which rounds up correctly.
I wouldn't use NumberFormat. It is pretty slow and complicated to use.
import java.math.BigDecimal;
public class Test {
public static void main(String... args) {
round(6.2088, 3);
round(6.2089, 3);
}
private static void round(double num, int numDecimal) {
BigDecimal bd = new BigDecimal(num);
BigDecimal bd2 = BigDecimal.valueOf(num);
System.out.println("new BigDecimal: " + bd);
System.out.println("BigDecimal.valueOf: " + bd2);
System.out.printf("%." + numDecimal + "f%n", num);
System.out.printf("%." + numDecimal + "f%n", bd);
System.out.printf("%." + numDecimal + "f%n", bd2);
System.out.printf("%f%n", round3(num));
System.out.printf("%s%n", round3(num));
System.out.printf("%f%n", bd.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%s%n", bd.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%f%n", bd2.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
System.out.printf("%s%n", bd2.setScale(numDecimal, BigDecimal.ROUND_HALF_UP));
}
private static double round3(double num) {
final double factor = 1e3;
return Math.round(num * factor) / factor;
}
}
prints with Java 8.
new BigDecimal: 6.208800000000000096633812063373625278472900390625
BigDecimal.valueOf: 6.2088
6.209
6.209
6.209
6.209000
6.209
6.209000
6.209
6.209000
6.209
new BigDecimal: 6.208899999999999863575794734060764312744140625
BigDecimal.valueOf: 6.2089
6.209
6.209
6.209
6.209000
6.209
6.209000
6.209
6.209000
6.209