我想要的是一种将双精度数转换为使用Half-up方法取整的字符串的方法-即,如果要取整的小数为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实现此目标的最佳方法是什么?
#1楼
正如其他一些人指出的那样,正确的答案是使用DecimalFormat或BigDecimal 。 浮点没有小数所以你不可能圆/截形到他们摆在首位的具体数量。 您必须使用十进制基数,这就是这两个类的作用。
我将以下代码作为该线程中所有答案的反示例,甚至在整个StackOverflow(以及其他地方)上发布,以作为建议,建议相乘然后截断再除法。 该技术的倡导者有责任解释为什么以下代码在超过92%的情况下会产生错误的输出。
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
该程序的输出:
10001 trials 9251 errors
编辑:为了解决下面的一些评论,我使用BigDecimal和new MathContext(16)测试循环的模数部分,如下所示:
public static void main(String[] args)
{
int count = 0, errors = 0;
int scale = 2;
double factor = Math.pow(10, scale);
MathContext mc = new MathContext(16, RoundingMode.DOWN);
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
d = Math.round(d * factor) / factor;
BigDecimal bd = new BigDecimal(d, mc);
bd = bd.remainder(new BigDecimal("0.01"), mc);
if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
{
System.out.println(d + " " + bd);
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
结果:
10001 trials 4401 errors
#2楼
Real的Java How-to 发布了此解决方案,该解决方案也与Java 1.6之前的版本兼容。
BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
#3楼
假设value是double ,则可以执行以下操作:
(double)Math.round(value * 100000d) / 100000d
这是5位数字的精度。 零数表示小数位数。
#4楼
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
#5楼
使用setRoundingMode ,显式设置RoundingMode以处理半个半个回合的问题,然后将格式模式用于所需的输出。
例:
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.77395这样的值实际上在内部没有该确切值。 它可以稍大或略小。 如果内部值稍小,则不会舍入到2.7740。 为了解决这种情况,您需要了解所使用的值的准确性,并在舍入之前添加或减去该值。 例如,当您知道自己的值在6位数字之内是正确的,然后将其四舍五入时,请将精度添加到值中:
Double d = n.doubleValue() + 1e-6;
要舍入,请减去精度。