我想要的是一种将双精度转换为字符串的方法,该字符串使用半向上舍入方法进行舍入-即,如果要舍入的小数为5,则始终向上舍入到下一个数字。这是大多数人在大多数情况下所期望的四舍五入的标准方法。

我还希望只显示有效数字,即不应有任何尾随零。

我知道这样做的一种方法是使用String.format方法:

String.format("%.5g%n", 0.912385);

返回:

0.91239

这是很好的,但是它总是显示带有5位小数的数字,即使它们不重要:

String.format("%.5g%n", 0.912300);

返回:

0.91230

另一种方法是使用DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

返回:

0.91238

然而,正如您所见,这使用了半偶数舍入。也就是说,如果前一个数字是偶数,它将向下舍入。我想要的是:

0.912385 -> 0.91239
0.912300 -> 0.9123

在Java中实现这一点的最佳方法是什么?


当前回答

非常简单的方法

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();

    DecimalFormat deciFormat = new DecimalFormat();
    deciFormat.setMaximumFractionDigits(places);
    String newValue = deciFormat.format(value);

    return Double.parseDouble(newValue);

}

double a = round(12.36545, 2);

其他回答

假设你有

double d = 9232.129394d;

可以使用BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

或不带BigDecimal

d = Math.round(d*100)/100.0d;

两种解决方案d==9232.13

使用setRoundingMode,显式设置Rounding模式以处理半偶数轮的问题,然后使用所需输出的格式模式。

例子:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

给出输出:

12
123.1235
0.23
0.1
2341234.2125

编辑:最初的答案没有提到双精度值的准确性。如果你不太在乎它是向上还是向下,那就好了。但如果您想要精确舍入,则需要考虑值的预期精度。浮点值在内部具有二进制表示。这意味着像2.7735这样的值实际上在内部没有精确的值。它可以稍大或稍小。如果内部值稍小,则不会舍入到2.7740。要纠正这种情况,您需要了解正在处理的值的准确性,并在舍入之前添加或减去该值。例如,当您知道您的值精确到6位数时,若要向上舍入中间值,请将该精度添加到值中:

Double d = n.doubleValue() + 1e-6;

要向下舍入,请减去精度。

double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;

Real的Java How to发布了这个解决方案,它也与Java 1.6之前的版本兼容。

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

UPDATE:BigDecimal.ROUND_HALF_UP已弃用-使用舍入模式

BigDecimal bd = new BigDecimal(Double.toString(number));
bd = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
return bd.doubleValue();
public static double formatDecimal(double amount) {
    BigDecimal amt = new BigDecimal(amount);
    amt = amt.divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_EVEN);
    return amt.doubleValue();
}

使用Junit进行测试

@RunWith(Parameterized.class)
public class DecimalValueParameterizedTest {

  @Parameterized.Parameter
  public double amount;

  @Parameterized.Parameter(1)
  public double expectedValue;

@Parameterized.Parameters
public static List<Object[]> dataSets() {
    return Arrays.asList(new Object[][]{
            {1000.0, 1000.0},
            {1000, 1000.0},
            {1000.00000, 1000.0},
            {1000.01, 1000.01},
            {1000.1, 1000.10},
            {1000.001, 1000.0},
            {1000.005, 1000.0},
            {1000.007, 1000.01},
            {1000.999, 1001.0},
            {1000.111, 1000.11}
    });
}

@Test
public void testDecimalFormat() {
    Assert.assertEquals(expectedValue, formatDecimal(amount), 0.00);
}