我想要的是一种将双精度转换为字符串的方法,该字符串使用半向上舍入方法进行舍入-即,如果要舍入的小数为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中实现这一点的最佳方法是什么?


当前回答

DecimalFormat是最好的输出方式,但我不喜欢它。我总是这样做,因为它返回双倍值。所以我可以使用它而不仅仅是输出。

Math.round(selfEvaluate*100000d.0)/100000d.0;

OR

Math.round(selfEvaluate*100000d.0)*0.00000d1;

如果需要大的小数位数值,可以改用BigDecimal。无论如何,0很重要。没有它,0.33333d5的舍入返回0.33333,只允许9位数字。没有.0的第二个函数有问题,返回0.3000000000000004。

其他回答

我来这里只是想得到一个关于如何舍入数字的简单答案。这是提供这一点的补充答案。

如何在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 ***

如果您使用的是JDK最小的技术。这里有一种没有任何Java库的方法:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

正如其他人所指出的,正确的答案是使用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和新的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

使用setRoundingMode,显式设置Rounding模式以处理半偶数轮的问题,然后使用所需输出的格式模式。

例子:

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.7735这样的值实际上在内部没有精确的值。它可以稍大或稍小。如果内部值稍小,则不会舍入到2.7740。要纠正这种情况,您需要了解正在处理的值的准确性,并在舍入之前添加或减去该值。例如,当您知道您的值精确到6位数时,若要向上舍入中间值,请将该精度添加到值中:

Double d = n.doubleValue() + 1e-6;

要向下舍入,请减去精度。

@米尔豪斯:舍入的十进制格式非常好:

您也可以使用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的优点在于它同时处理字符串外观-以及舍入精度设置的级别。埃尔戈:你以一个代码实现的价格获得两个好处