我该如何在Java中将一个double截断为仅保留两位小数?

125
例如,我有一个变量3.545555555,我想截取为3.54。

30
这不是重复内容。截断和四舍五入是完全不同的概念。 - markthegrea
同意,不知道为什么这被标记为重复。 - Bassinator
18个回答

173

如果您想要用于显示的目的,请使用java.text.DecimalFormat

 new DecimalFormat("#.##").format(dblVar);

如果需要进行计算,请使用java.lang.Math

 Math.floor(value * 100) / 100;

55
这并不会将3.545555555截断为3.54,而是四舍五入为3.55。需要设置DecimalFormat.setRoundingMode()为RoundingMode.FLOOR。 - Christian García
10
对我来说,这个不起作用 Math.floor(9.62 * 100) / 100 的结果是9.61。 - Mani
4
使用 floor 函数仅适用于正数的截断。 - Dev
5
正确的截断应使用“RoundingMode.DOWN”,因为“RoundingMode.FLOOR”总是朝负无穷大的方向舍入。 - Dev
第二种方法并不总是有效,请检查:Math.floor(2.28 * 100)/100 = 2.27。 - Carlos López Marí

47
DecimalFormat df = new DecimalFormat(fmt);
df.setRoundingMode(RoundingMode.DOWN);
s = df.format(d);

查看可用的RoundingModeDecimalFormat


没有回答问题,问题要求截断而不是四舍五入。 - Basil Bourque
8
RoundingMode.DOWN 指的是截断(直接删除小数部分)。与其他错误地推荐 floor 函数(floor 只对正数进行截断)不同,这个方法对于正数和负数都能正确地截断。 - Dev
1
@Dev 我错了。我现在明白DOWN确实会对正数和负数进行截断。可以在文档中的示例表中看到。 - Basil Bourque

31

其他答案都无法适用于正负值(我的意思是进行计算和只截断而不四舍五入),且不需要转换为字符串。

来自如何在Java中将数字四舍五入到n个小数位的链接。

private static BigDecimal truncateDecimal(double x,int numberofDecimals)
{
    if ( x > 0) {
        return new BigDecimal(String.valueOf(x)).setScale(numberofDecimals, BigDecimal.ROUND_FLOOR);
    } else {
        return new BigDecimal(String.valueOf(x)).setScale(numberofDecimals, BigDecimal.ROUND_CEILING);
    }
}

这种方法对我来说很有效。

System.out.println(truncateDecimal(0, 2));
    System.out.println(truncateDecimal(9.62, 2));
    System.out.println(truncateDecimal(9.621, 2));
    System.out.println(truncateDecimal(9.629, 2));
    System.out.println(truncateDecimal(9.625, 2));
    System.out.println(truncateDecimal(9.999, 2));
    System.out.println(truncateDecimal(-9.999, 2));
    System.out.println(truncateDecimal(-9.0, 2));

结果:

0.00
9.62
9.62
9.62
9.62
9.99
-9.99
-9.00

这是更适用于更多测试用例的答案。 - diego matos - keke
2
return new BigDecimal(x).setScale(numberofDecimals, RoundingMode.DOWN).doubleValue(); 返回new BigDecimal(x).setScale(numberofDecimals, RoundingMode.DOWN).doubleValue(); - Shimon Doodkin
似乎你想表达的是向下取整:https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html - Shimon Doodkin
@ShimonDoodkin 要使用 String.valueOf(x),否则在例如2.28、9.62或9411.3上它会工作不正确。 - Dima Korobskiy

9

将其格式化为字符串,再转换回double类型,我觉得这样可以得到您想要的结果。

double值不会使用round()、floor()或ceil()进行处理。

一个快速的解决方法可能是:

 String sValue = (String) String.format("%.2f", oldValue);
 Double newValue = Double.parseDouble(sValue);

您可以使用sValue进行显示,或使用newValue进行计算。

1
它将数值四舍五入到最接近的值。例如:当我这样做时,0.018 被转换为 0.02。 - karan patel

9

首先要注意,double是一个二进制小数,实际上并没有十进制位。

如果你需要十进制位,可以使用BigDecimal,它有一个setScale()方法用于截断,或者使用DecimalFormat获得一个String


7
如果出于任何原因,您不想使用 BigDecimal ,您可以将您的 double 强制转换为 int 以截断它。
如果要截断到个位:
- 简单地强制转换为 int 如果要截断到十分位:
- 乘以十 - 强制转换为 int - 再次转换为 double - 并除以十。
如果要截断到百分位:
- 乘以一百并除以一百等等。
例如:
static double truncateTo( double unroundedNumber, int decimalPlaces ){
    int truncatedNumberInt = (int)( unroundedNumber * Math.pow( 10, decimalPlaces ) );
    double truncatedNumber = (double)( truncatedNumberInt / Math.pow( 10, decimalPlaces ) );
    return truncatedNumber;
}

在这个例子中,decimalPlaces代表你需要四舍五入到哪一个数位之后,例如1表示四舍五入到小数点后一位即十分位,2表示四舍五入到小数点后两位即百分位,以此类推(0表示四舍五入到整数位,-1表示四舍五入到十位,以此类推)。

4
这是一个非常糟糕的通用解决方案。如果“unroundedNumber”足够大,乘法和转换都将丢失数据,并导致实际上是随机数。它可能适用于问题中的示例,但通用解决方案应适用于任何“double”。 - blm
在例如2.28、9.62或9411.3上无法正常工作。 - Dima Korobskiy

6
您可以使用NumberFormat类对象来完成这项任务。
// Creating number format object to set 2 places after decimal point
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);            
nf.setGroupingUsed(false);

System.out.println(nf.format(precision));// Assuming precision is a double type variable

我还会添加 nf.setMinimumFractionDigits(2); - Amir Dora.

5

将3.545555555变为3.54。 尝试以下方法:

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

    df.setRoundingMode(RoundingMode.FLOOR);

    double result = new Double(df.format(3.545555555);

这将会得到 3.54!


但是如果我将该值设置为3.50123,我将得到3.5。 - Cyrus
1
@Cyrus 3.5是对于3.50123的正确结果。trunc(2)函数应该将其截断为3.50。 - Dima Korobskiy

2

我使用了 Math.floor() 方法和基本的小数移位(100 = 2)。

//3.545555555 to 3.54 by floor method
double x = 3.545555555;
double y = Math.floor(x * 100); //354
double z = y / 100; //3.54

2
也许可以使用Math.floor(value * 100) / 100?需要注意的是,像3.54这样的值可能无法准确表示为一个double类型。

不幸的是,有反例表明这种方法行不通:double value = 9411.3; System.out.println(Math.floor(value * 100) / 100); // 9411.29 - Dima Korobskiy
@DKroot:这取决于你对“正确”的定义。当你写 double value = 2.28; 时,实际上 value 的值是 2.279999999999999804600747665972448885440826416015625,因此在截断后你会得到一个诚实的 2.27。从我的角度来看,问题不在于截断,而在于赋值。 - Vlad
1
我知道二进制精度丢失的问题。这就是为什么 trunc() 是一种危险的操作。 然而,所谓“正确”,是指“从用户/数学角度预期的结果”:例如,assert(2.28.trunc(2)) == 2.28,其中 trunc(precision) 是一种截断方法。例如,请参见此博客文章(不是我的):https://blog.mrhaki.com/2010/01/groovy-goodness-round-and-truncate.html - Dima Korobskiy
@DKroot:从一个天真用户的角度来看,代码中的“double d = 2.28;”这一行表现不佳。你必须知道,实际上并不存在2.28这样的双精度数字,对吧?我的代码将2.279999...作为输入,但它并不知道(也不应该知道!)这个参数来自于不可靠的假设,即它实际上应该是十进制的2.28。代码获取的数字严格小于2.28,并产生了正确的结果。 - Vlad
@DKroot:关于您提到的博客文章中的Groovy示例:https://groovyide.com/cpi/share/v1/VVA7b8MgEP4rlClRLAZPlaNuHbJk7cRC4OIi4TvEo1Vk8d97dp0mZeG-x30cN0uPsRY5zPJC7iYHKTv5CcZBygtJwZ1WxMoJQqAX2ToZE0VIxcPiaUxkm3xcUyw5YK-fIqUiLE0qm6hGU-Db3JSPylICZWsuNDn4UrX4oM6QsxnhqFGjg6vYsOB3LJfvppjdnZt-773GWaPgszQ4qpcAHyZUEG-iV_2rOz7UkipansCx9uRUK7_r95s1Jo8loNCSkh89mjBwLQ7_0g9MdY_EzfGHt6gEpSa8z8pk08iLvbKreEJe0NPXZGs_。 - Vlad
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接