我想要的是一种将双精度转换为字符串的方法,该字符串使用半向上舍入方法进行舍入-即,如果要舍入的小数为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中实现这一点的最佳方法是什么?


当前回答

@米尔豪斯:舍入的十进制格式非常好:

您也可以使用DecimalFormat df=新的DecimalFormat(“#.000000”);df.格式(0.912385);以确保后面有0。

我想补充一点,这种方法非常善于提供数字、舍入机制-不仅是视觉上的,也是处理时的。

假设:您必须在GUI中实现舍入机制程序只需更改结果输出的精度更改插入符号格式(即在括号内)。以便:

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

将作为输出返回:0.912385

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

将作为输出返回:0.91239

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

将返回为输出:0.9124

[EDIT:如果插入符号格式是这样的(“#0.#############”)为参数起见,输入小数,例如3.1415926,DecimalFormat不产生任何垃圾(例如尾随零),并将返回:3.1415926…如果你有这种倾向。当然,这有点冗长对于一些开发人员来说-但是,嘿,它的内存占用量很低在处理过程中,并且非常容易实现。]

因此本质上,DecimalFormat的优点在于它同时处理字符串外观-以及舍入精度设置的级别。埃尔戈:你以一个代码实现的价格获得两个好处

其他回答

请记住,String.format()和DecimalFormat使用默认Locale生成字符串。因此,他们可以用点或逗号作为整数和小数部分之间的分隔符来编写格式化的数字。要确保舍入字符串的格式为所需的格式,请按如下方式使用java.text.NumberFormat:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

将以英语语言环境打印(无论您的语言环境如何):0.99123.57123

示例取自Farenda-如何正确地将double转换为String。

使用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 x = 123.123;
System.out.printf( "%.2f", x );

如果您真的想要十进制数字用于计算(而不仅仅用于输出),请不要使用基于二进制的浮点格式,如double。

Use BigDecimal or any other decimal-based format.

我确实使用BigDecimal进行计算,但请记住,它取决于你正在处理的数字。在我的大多数实现中,我发现从double或整数到Long足以进行非常大的数值计算。

事实上,我已经最近使用解析为Long以获得准确的表示(与十六进制结果相反)在GUI中,对于大小为#############字符的数字(作为示例)。

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);
}