1. 概述

在这个教程中,我们将探讨Java中截断double到两位小数的各种方法。我们将看到将结果转换为String的方法,以及返回Number对象的选项。

2. 使用Math.floor()Math.ceil()四舍五入

我们将首先研究使用Math类去除多余小数位的方法。要将正数截断到两位小数,我们首先将double乘以100,将所有保留的小数位移动到小数点之前。接着,使用Math.floor()向下取整,移除小数点后的所有部分。最后,除以100来撤销之前的乘法:

@Test
void givenADouble_whenUsingMath_truncateToTwoDecimalPlaces(){
    double positive = 1.55555555;
    double truncated = Math.floor(positive * 100) / 100;
    assertEquals("1.55", String.valueOf(truncated));

    double negative = -1.55555555;
    double negativeTruncated = Math.ceil(negative * 100) / 100;
    assertEquals("-1.55", String.valueOf(negativeTruncated));
}

对于负数,过程几乎相同,但我们会用Math.ceil()向上取整。如果我们希望自动检测double是正还是负,并根据需要使用正确的方法,可以添加额外的代码。

如果需要保留更多或更少的小数位,只需在乘法和除法中增加或减少零。例如,为了保留三位小数,我们将乘以和除以1000。这种方法在我们需要将double保持为double,而不是将其转换为String时很有用。

3. 使用String.format()

接下来,我们将看看一些设计用于显示而非计算的目的方法。这些方法会返回String,但我们始终可以将结果转换回double,如果需要的话。String.format()方法接受两个参数:我们想要应用的格式和格式字符串中的引用参数。为了截断到两位小数,我们将使用格式字符串"%.2f"

@Test
void givenADouble_whenUsingStringFormat_truncateToTwoDecimalPlaces() {
    double positive = 1.55555555;
    String truncated = String.format("%.2f", positive);
    assertEquals("1.56", truncated);

    double negative = -1.55555555;
    String negativeTruncated = String.format("%.2f", negative);
    assertEquals("-1.56", negativeTruncated);
}

格式字符串末尾的f指示格式化器生成小数格式,而.2表示我们希望小数点后有两位。我们可以根据需要调整这个值来截断到所需的位数。测试显示结果实际上进行了向上舍入,而不是截断,因此这可能不适合我们的需求。

4. 使用NumberFormat创建String

NumberFormat是一个抽象类,旨在让我们格式化任何数字。由于它是一个抽象类,我们需要先使用getNumberInstance()获取一个可以使用的对象。注意,如果没有特别指定,它将使用默认的本地化。然后,我们可以使用setMaximumFractionDigits()指定我们想要的小数位数。最后,因为我们要进行截断而不是四舍五入,所以我们使用setRoundingMode()并传入RoundingMode.DOWN参数:

@Test
public void givenADouble_whenUsingNumberFormat_truncateToTwoDecimalPlaces(){
    NumberFormat nf = NumberFormat.getNumberInstance();
    nf.setMaximumFractionDigits(2);
    nf.setRoundingMode(RoundingMode.DOWN);

    double value = 1.55555555;
    String truncated = nf.format(value);
    assertEquals("1.55", truncated);

    double negativeValue = -1.55555555;
    String negativeTruncated = nf.format(negativeValue);
    assertEquals("-1.55", negativeTruncated);
}

有了NumberFormat的设置,我们只需调用format()方法并传入我们的double即可。在上述测试中,我们可以看到它对正负数处理得同样好。

5. 使用DecimalFormat创建String

DecimalFormatNumberFormat的一个子类,专为小数设计。它是具体的类,所以可以直接创建一个实例,通过构造函数传递我们想要的模式。这里我们将传递“#.##.”,小数点后的数量号表示要保留多少位:

@Test
public void givenADouble_whenUsingDecimalFormat_truncateToTwoDecimalPlaces(){
    DecimalFormat df = new DecimalFormat("#.##");
    df.setRoundingMode(RoundingMode.DOWN);

    double value = 1.55555555;
    String truncated = df.format(value);
    assertEquals("1.55", truncated);

    double negativeValue = -1.55555555;
    String negativeTruncated = df.format(negativeValue);
    assertEquals("-1.55", negativeTruncated);
}

与之前的NumberFormat一样,我们也指定了RoundingMode.DOWN的使用。再次看到这个解决方案对正负数处理得很好,使其很有用。

6. 使用BigDecimal获得最优精度

Java的BigDecimal类最适合直接处理小数位的截断,同时保持结果为我们可以进行进一步计算的数字。如果可能,使用BigDecimal代替double可能是最佳选择。我们可以将double值传递给构造函数,同时直接告诉它保留两位小数并向下取整:

@Test
void givenADouble_whenUsingBigDecimal_truncateToTwoDecimalPlaces(){
    BigDecimal positive = new BigDecimal(2.555555).setScale(2, RoundingMode.DOWN);
    BigDecimal negative = new BigDecimal(-2.555555).setScale(2, RoundingMode.DOWN);
    assertEquals("2.55", positive.toString());
    assertEquals("-2.55", negative.toString());
}

我们在测试中将结果转换为String以视觉上清晰地展示结果。然而,如果我们想继续使用截断后的输出进行进一步计算,我们可以这样做。

7. 总结

我们已经讨论了Java中截断double的五种不同方法。我们了解到,String.format(),NumberFormat,和DecimalFormat在用于展示目的时可用,因为它们会输出String。当然,我们总是可以使用Double.parseDouble()String转换回double。或者,我们可以使用Math类或BigDecimal来保持截断后的数值作为可以进行进一步计算的数字。