我想要的是一种将双精度转换为字符串的方法,该字符串使用半向上舍入方法进行舍入-即,如果要舍入的小数为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中实现这一点的最佳方法是什么?
当尝试舍入到小数位数的负数时,Math.round解决方案存在问题。考虑代码
long l = 10;
for(int dp = -1; dp > -10; --dp) {
double mul = Math.pow(10,dp);
double res = Math.round(l * mul) / mul;
System.out.println(""+l+" rounded to "+dp+" dp = "+res);
l *=10;
}
这是结果
10 rounded to -1 dp = 10.0
100 rounded to -2 dp = 100.0
1000 rounded to -3 dp = 1000.0
10000 rounded to -4 dp = 10000.0
100000 rounded to -5 dp = 99999.99999999999
1000000 rounded to -6 dp = 1000000.0
10000000 rounded to -7 dp = 1.0E7
100000000 rounded to -8 dp = 1.0E8
1000000000 rounded to -9 dp = 9.999999999999999E8
当1除以1.0E-5时会出现小数位数为-5的问题,这是不精确的。
可以使用
double mul = Math.pow(10,dp);
double res;
if(dp < 0 ) {
double div = Math.pow(10,-dp);
res = Math.round(l * mul) *div;
} else {
res = Math.round(l * mul) / mul;
}
但这是使用BigDecimal方法的另一个原因。
因此,在阅读了大部分答案后,我意识到其中大多数答案都不精确,事实上,使用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的预期产量。
试试看:org.apache.commons.math3.util.Precision.round(双x,int scale)
参见:http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html
Apache Commons数学图书馆主页:http://commons.apache.org/proper/commons-math/index.html
该方法的内部实现是:
public static double round(double x, int scale) {
return round(x, scale, BigDecimal.ROUND_HALF_UP);
}
public static double round(double x, int scale, int roundingMethod) {
try {
return (new BigDecimal
(Double.toString(x))
.setScale(scale, roundingMethod))
.doubleValue();
} catch (NumberFormatException ex) {
if (Double.isInfinite(x)) {
return x;
} else {
return Double.NaN;
}
}
}
我来这里只是想得到一个关于如何舍入数字的简单答案。这是提供这一点的补充答案。
如何在Java中舍入数字
最常见的情况是使用Math.rround()。
Math.round(3.7) // 4
数字四舍五入到最接近的整数。A.5值向上舍入。如果需要不同的舍入行为,可以使用其他数学函数之一。请参阅下面的比较。
圆形的
如上所述,这四舍五入到最接近的整数。5位小数向上舍入。此方法返回int。
Math.round(3.0); // 3
Math.round(3.1); // 3
Math.round(3.5); // 4
Math.round(3.9); // 4
Math.round(-3.0); // -3
Math.round(-3.1); // -3
Math.round(-3.5); // -3 *** careful here ***
Math.round(-3.9); // -4
ceil
任何十进制值都将舍入到下一个整数。它通向天花板。此方法返回一个double。
Math.ceil(3.0); // 3.0
Math.ceil(3.1); // 4.0
Math.ceil(3.5); // 4.0
Math.ceil(3.9); // 4.0
Math.ceil(-3.0); // -3.0
Math.ceil(-3.1); // -3.0
Math.ceil(-3.5); // -3.0
Math.ceil(-3.9); // -3.0
地板
任何十进制值都向下舍入到下一个整数。此方法返回一个double。
Math.floor(3.0); // 3.0
Math.floor(3.1); // 3.0
Math.floor(3.5); // 3.0
Math.floor(3.9); // 3.0
Math.floor(-3.0); // -3.0
Math.floor(-3.1); // -4.0
Math.floor(-3.5); // -4.0
Math.floor(-3.9); // -4.0
rint
这与舍入相似,因为十进制值舍入到最接近的整数。然而,与舍入不同,.5值舍入为偶数。此方法返回一个double。
Math.rint(3.0); // 3.0
Math.rint(3.1); // 3.0
Math.rint(3.5); // 4.0 ***
Math.rint(3.9); // 4.0
Math.rint(4.5); // 4.0 ***
Math.rint(5.5); // 6.0 ***
Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***
@米尔豪斯:舍入的十进制格式非常好:
您也可以使用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的优点在于它同时处理字符串外观-以及舍入精度设置的级别。埃尔戈:你以一个代码实现的价格获得两个好处