我想要的是一种将双精度转换为字符串的方法,该字符串使用半向上舍入方法进行舍入-即,如果要舍入的小数为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中实现这一点的最佳方法是什么?
因此,在阅读了大部分答案后,我意识到其中大多数答案都不精确,事实上,使用BigDecimal似乎是最佳选择,但如果你不了解RoundingMode的工作原理,你将不可避免地失去精度。我在一个项目中处理大数字时发现了这一点,并认为这可以帮助其他有舍入问题的人。例如
BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());
您希望得到1363.28作为输出,但如果您不知道RoundingMode在做什么,则最终会得到1363.27,这是不期望的。因此,查看Oracle文档,您将发现RoundingMode.HALF_UP的以下描述。
四舍五入模式向“最近邻居”舍入,除非两者都邻居是等距的,在这种情况下是四舍五入的。
所以知道了这一点,我们意识到除非我们想向最近的邻居取整,否则我们不会得到精确的舍入。因此,为了完成一个足够的循环,我们需要从n-1小数循环到所需的小数位数。例如
private double round(double value, int places) throws IllegalArgumentException {
if (places < 0) throw new IllegalArgumentException();
// Cast the number to a String and then separate the decimals.
String stringValue = Double.toString(value);
String decimals = stringValue.split("\\.")[1];
// Round all the way to the desired number.
BigDecimal bd = new BigDecimal(stringValue);
for (int i = decimals.length()-1; i >= places; i--) {
bd = bd.setScale(i, RoundingMode.HALF_UP);
}
return bd.doubleValue();
}
这将最终为我们提供1363.28的预期产量。
我同意使用DecimalFormat或BigDecimal的答案。
请先阅读下面的更新!
但是,如果您确实希望舍入双值并获得双值结果,则可以使用上述org.apache.commons.math3.util.Precision.round(..)。该实现使用BigDecimal,速度慢,并且会产生垃圾。
decimal4j库中的DoubleRounder实用程序提供了一种类似但快速且无垃圾的方法:
double a = DoubleRounder.round(2.0/3.0, 3);
double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
double c = DoubleRounder.round(1000.0d, 17);
double d = DoubleRounder.round(90080070060.1d, 9);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
将输出
0.667
0.666
1000.0
9.00800700601E10
看见https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility
披露:我参与了decimal4j项目。
更新:正如@iaforek指出的那样,DoubleRounder有时会返回违反直觉的结果。原因是它执行数学上正确的舍入。例如,DoubleRounder.round(256.025d,2)将向下舍入到256.02,因为表示为256.025d的双精度值略小于有理值256.025,因此将向下舍入。
笔记:
这种行为与BigDecimal(double)构造函数非常相似(但与使用字符串构造函数的valueOf(double)不同)。这个问题可以先用双倍的舍入步骤以更高的精度来解决,但它很复杂,我不打算在这里讨论细节
出于这些原因以及本文中提到的所有内容,我不建议使用DoubleRounder。