如何在Java中将数字四舍五入到小数点后n位


1258

我想要的是一种使用double-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实现此目标的最佳方法是什么?

Answers:


751

使用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.7735这样的值实际上在内部没有该确切值。它可以稍大或略小。如果内部值稍小,则不会舍入到2.7740。为了解决这种情况,您需要了解所使用的值的准确性,并在舍入之前添加或减去该值。例如,当您知道自己的值在6位数字之内是准确的,然后将其四舍五入到该中间值时,将该精度加到该值上:

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

要四舍五入,请减去精度。


9
这可能是到目前为止提出的最好的解决方案。当我初次查看DecimalFormat类时没有发现此功能的原因是它仅在Java 1.6中引入。不幸的是,我只能使用1.5,但是将来了解它会很有用。
亚历克斯·斯普林

1
我尝试了:"#.##",四舍五入HALF_UP256.335f-> "256.33"...(示例来自@asterite答案的注释)。
bigstones

6
请小心,因为DecimalFormat取决于您当前的本地配置,您可能不会得到一个点作为分隔符。我个人更喜欢下面的Asterite答案
Gomino

1
还应注意,您不应期望DecimalFormat是线程安全的。根据Java文档十进制格式通常不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一种格式,则必须在外部进行同步。
CGK

1
我该如何做才能正确舍入,因此不会将0.0004舍入到0.001

471

假设valuedouble,您可以执行以下操作:

(double)Math.round(value * 100000d) / 100000d

这是5位数字的精度。零数表示小数位数。


71
更新:我刚刚确认这样做比使用DecimalFormat更快。我使用DecimalFormat循环了200次,并且使用了这种方法。DecimalFormat花费14ms完成200个循环,此方法花费不到1ms。正如我所怀疑的那样,这更快。如果您在时钟周期内获得报酬,这就是您应该做的。我很惊讶克里斯·库德莫尔(Chris Cudmore)甚至会说他说的诚实。分配对象总是比强制转换基元和使用静态方法(Math.round()相对于decimalFormat.format())昂贵。
安迪·杰伊

99
该技术在超过90%的情况下失败。-1。
user207421

25
事实上,这种失败:Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775
罗伯特·图珀洛-施内克

54
使用此方法(或任何四舍五入的浮点数)时要非常小心。它仅适用于265.335失败。中间结果265.335 * 100(2位精度)是26533.499999999996。这意味着它向下舍入到265.33。从浮点数转换为实数十进制数时,仅存在固有的问题。请在stackoverflow.com/a/12684082/144578上
Sebastiaan van den Broek 2013年

6
@SebastiaanvandenBroek:哇,我从来不知道得到一个错误答案很容易。但是,如果使用非精确数字,则必须认识到任何值都不精确265.335真正的意思265.335 += tolerance是容差取决于先前的操作和输入值的范围。我们不知道真实的确切价值。在边缘值处,任何一个答案都可以说是正确的。如果需要准确,我们不应该双重工作。在fail这里是不是在转换回双。在OP中,他认为自己可以完全依靠输入265.335
制造商史蒂夫2015年

191
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

会给你一个BigDecimal。要从中获取字符串,只需调用BigDecimaltoString方法,或toPlainString为纯格式字符串调用Java 5+ 的方法。

示例程序:

package trials;
import java.math.BigDecimal;

public class Trials {

    public static void main(String[] args) {
        int yourScale = 10;
        System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
    }

38
那是我的首选解决方案。甚至更短:BigDecimal.valueOf(doubleVar).setScale(yourScaleHere,BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf(double val)实际上在后台调用Double.toString();)
Etienne Neveu 2010年

4
真好 不要走捷径和使用,new BigDecimal(doubleVar)因为这样会遇到浮点取整的问题
Edd 2015年

8
@Edd,有趣的是,舍入问题发生在SebastiaanvandenBroek在评论asterite的答案时提到的情况。double val = 265.335;BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.34,但是(new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.33
制造商史蒂夫2015年

7
@ToolmakerSteve那是因为将新的BigDecimal与double一起使用会直接使用double值,并尝试使用它来创建BigDecimal,而在使用BigDecimal.valueOf或tostring形式时,会先将其解析为字符串(更精确的表示形式) 。
MetroidFan2002 2015年

116

您也可以使用

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

确保您具有尾随的0。


25
我认为问题的目标之一是,“应该不会是任何尾随零”。
午餐盒2012年

7
对于这个问题,操作员不想要零,但这正是我想要的。如果你有一个数字跟3位小数的列表,你希望他们都具有相同的数字,即使是0
汤姆·金凯德

您忘了指定RoundingMode.
IgorGanapolsky

1
@IgorGanapolsky默认Decimal mode使用RoundingMode.HALF_EVEN.
EndermanAPM

87

正如其他人指出的那样,正确的答案是使用DecimalFormatBigDecimal。浮点不具有小数所以你不可能圆/截形到他们摆在首位的具体数量。您必须使用十进制基数,这就是这两个类的作用。

我将以下代码作为该线程中所有答案的反示例,甚至在整个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

编辑:为了解决下面的一些评论,我使用BigDecimalnew 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

8
诀窍在于,在所有9251错误中,打印结果仍然正确。
Didier L

7
@DidierL这并不令我惊讶。我非常幸运地将“数值方法”作为我的第一门计算课程,并在一开始就被介绍了浮点可以做什么和不能做什么。大多数程序员对此都很含糊。
user207421

15
您所要做的只是反驳浮点数不能精确表示许多十进制值,我希望我们都可以理解。并不是说舍入会引起问题。如您所知,数字仍然按预期打印。
彼得·劳瑞

8
您的测试已损坏,请执行round(),并且测试有94%的时间失败。ideone.com/1y62CY打印100 trials 94 errors您应该先通过一个通过的测试,并证明引入舍入会破坏测试。
彼得·劳瑞

6
驳斥,这里驳斥。ideone.com/BVCHh3double
Peter Lawrey 2014年

83

假设你有

double d = 9232.129394d;

您可以使用 BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

是否有BigDecimal

d = Math.round(d*100)/100.0d;

两种解决方案 d == 9232.13


2
我认为这是Java 1.5(及以下)用户的最佳解决方案。一个注释,不要使用HALF_EVEN舍入模式,因为它具有奇数和偶数的差异行为(例如2.5舍入为2,而5.5舍入为6),除非您要这样做。
IcedD​​ante 2012年

4
第一个解决方案是正确的:第二个解决方案无效。请参见此处以获取证明。
user207421

1
@EJP:甚至第一个解决方案RoundingMode.HALF_UP也是错误的。尝试一下1.505。正确的方法是使用BigDecimal.valueOf(d)
Matthias Braun

Matthias Braun,解决方案很好,因此可以使用31 ups。.如果要将1.505转换为双精度至十进制,则将1.505十进制存储为1.50499998双精度浮点数,然后必须首先将其转换为Double.toString(x)然后将其放入BigDecimal()中,但速度非常慢,并且首先破坏了使用double来提高速度的目的。
hamish

1
用BigDecimal(take 225 ms)和Math.round(2 ms)方式运行100k循环,这是计时...花费时间:225毫秒转换为使用:9232.13花费时间:2毫秒转换为:9232.13 techiesinfo.com
user1114134

59

您可以使用DecimalFormat类。

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

有什么理由Double.valueOf()被选中Double.parseDouble()?该valueOf()方法返回一个Double对象,而parseDouble()将返回一个double原语。通过编写当前代码的方式,您还可以对返回值应用自动拆箱,以将其强制转换为twoDouble变量期望的原语,这是一个额外的字节码操作。我会改用答案parseDouble()
ecbrodie

Double.parseDouble()需要String输入。
莱德(Ryde)

38

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();

33
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;

2
是的,这正是math.round对正数所做的,但是您是否尝试过对负数进行此操作?人们在其他解决方案中使用math.round来涵盖负数的情况。
hamish

注意:Math.floor(x + 0.5)以及Math.round(x)
Peter Lawrey,

30

@Milhous:十进制的十进制格式非常好:

您也可以使用

DecimalFormat df = new DecimalFormat("#.00000");
df.format(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

[编辑:如果插入符格式也是如此(“#0。#############”),并且您输入了一个小数,例如3.1415926,出于参数的考虑,DecimalFormat不会产生任何垃圾(例如尾随零),然后返回: 3.1415926..(如果您这样倾斜)。当然,对于某些开发人员而言,它有点冗长-但是,嘿,它在处理过程中的内存占用量很小,并且非常易于实现。]

因此,从本质上讲,DecimalFormat的优点在于,它可以同时处理字符串外观-以及舍入精度设置的级别。因此:以一个代码实现的价格,您将获得两个好处。;)


2
如果您确实需要十进制数字进行计算(不仅用于输出),请不要使用基于二进制的浮点格式,例如double。使用BigDecimal或任何其他基于十进制的格式。
圣保罗Ebermann

20

以下是如果希望将结果作为字符串使用的方法的摘要:

  1. DecimalFormat#setRoundingMode()

    DecimalFormat df = new DecimalFormat("#.#####");
    df.setRoundingMode(RoundingMode.HALF_UP);
    String str1 = df.format(0.912385)); // 0.91239
  2. BigDecimal#setScale()

    String str2 = new BigDecimal(0.912385)
        .setScale(5, BigDecimal.ROUND_HALF_UP)
        .toString();

这是关于如果需要的话可以使用哪些库的建议double。不过,我不建议将其用于字符串转换,因为double可能无法准确代表您想要的内容(例如请参见此处):

  1. Apache Commons Math的精度

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
  2. Colt的功能

    double rounded = Functions.round(0.00001).apply(0.912385)
  3. 来自Weka的实用程序

    double rounded = Utils.roundDouble(0.912385, 5)

18

您可以使用以下实用程序方法-

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

@mariolpantunes:它将失败。试试这个:round(1.005,2);round(0.50594724957626620092, 20);
Matthias Braun 2014年

有用。但是,毫无疑问,float和double是近似值。让我们考虑您的第一个例子。如果在Math.round之前打印感兴趣的InZeroDP的输出,它将打印100.49999999999999。你失去精度等Math.round绕着它为100由于自然或花车和双打有边界情况下,当它没有(更多信息正常工作en.wikipedia.org/wiki/Floating_point#Accuracy_problems
mariolpantunes

双倍快!小数点很慢。计算机不会费心地以十进制表示法处理他们的思维。您必须放弃一些小数精度以保持浮点数快速翻倍。
hamish

@hamish问题是关于精度,而不是速度。
user207421 '16



7

试试这个:org.apache.commons.math3.util.Precision.round(double x,int scale)

请参阅:http : //commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Apache Commons Mathematics Library主页是: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;
        }
    }
}

7

由于我没有找到关于该主题的完整答案,因此我建立了一个应该正确处理此问题的类,并提供以下支持:

  • 格式化:轻松地将双精度型格式化为具有一定小数位数的字符串
  • 解析:将格式化的值解析回两倍
  • 语言环境:使用默认语言环境设置格式和解析
  • 指数表示法:在特定阈值之后开始使用指数表示法

用法很简单

(为了这个示例,我使用了自定义语言环境)

public static final int DECIMAL_PLACES = 2;

NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);

String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"

double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345

这是课程

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;

public class NumberFormatter {

    private static final String SYMBOL_INFINITE           = "\u221e";
    private static final char   SYMBOL_MINUS              = '-';
    private static final char   SYMBOL_ZERO               = '0';
    private static final int    DECIMAL_LEADING_GROUPS    = 10;
    private static final int    EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
    private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation

    private DecimalFormat decimalFormat;
    private DecimalFormat decimalFormatLong;
    private DecimalFormat exponentialFormat;

    private char groupSeparator;

    public NumberFormatter(int decimalPlaces) {
        configureDecimalPlaces(decimalPlaces);
    }

    public void configureDecimalPlaces(int decimalPlaces) {
        if (decimalPlaces <= 0) {
            throw new IllegalArgumentException("Invalid decimal places");
        }

        DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
        separators.setMinusSign(SYMBOL_MINUS);
        separators.setZeroDigit(SYMBOL_ZERO);

        groupSeparator = separators.getGroupingSeparator();

        StringBuilder decimal = new StringBuilder();
        StringBuilder exponential = new StringBuilder("0.");

        for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
            decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
        }

        for (int i = 0; i < decimalPlaces; i++) {
            decimal.append("#");
            exponential.append("0");
        }

        exponential.append("E0");

        decimalFormat = new DecimalFormat(decimal.toString(), separators);
        decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
        exponentialFormat = new DecimalFormat(exponential.toString(), separators);

        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
        exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
    }

    public String format(double value) {
        String result;
        if (Double.isNaN(value)) {
            result = "";
        } else if (Double.isInfinite(value)) {
            result = String.valueOf(SYMBOL_INFINITE);
        } else {
            double absValue = Math.abs(value);
            if (absValue >= 1) {
                if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
                    value = Math.floor(value);
                    result = exponentialFormat.format(value);
                } else {
                    result = decimalFormat.format(value);
                }
            } else if (absValue < 1 && absValue > 0) {
                if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
                    result = decimalFormat.format(value);
                    if (result.equalsIgnoreCase("0")) {
                        result = decimalFormatLong.format(value);
                    }
                } else {
                    result = exponentialFormat.format(value);
                }
            } else {
                result = "0";
            }
        }
        return result;
    }

    public String formatWithoutGroupSeparators(double value) {
        return removeGroupSeparators(format(value));
    }

    public double parse(String value, double defValue) {
        try {
            return decimalFormat.parse(value).doubleValue();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return defValue;
    }

    private String removeGroupSeparators(String number) {
        return number.replace(String.valueOf(groupSeparator), "");
    }

}

7

如果您确实希望使用十进制数进行计算(不仅是输出),请不要使用像double这样的基于二进制的浮点格式。

Use BigDecimal or any other decimal-based format.

我确实使用BigDecimal进行计算,但是请记住,它取决于您要处理的数字的大小。在我的大多数实现中,我发现从双精度或整数到Long的解析足以满足大量计算的需要。

实际上,我最近在GUI中使用了Long-parsed-to-Long来获取准确的表示形式(与十六进制结果相反),数字的大小与################# ###############个字符(例如)。


6

为此,我们可以使用以下格式化程序:

 DecimalFormat df = new DecimalFormat("#.00");
 String resultado = df.format(valor)

要么:

DecimalFormat df = new DecimalFormat("0.00"); :

使用此方法总是获得两个小数:

   private static String getTwoDecimals(double value){
      DecimalFormat df = new DecimalFormat("0.00"); 
      return df.format(value);
    }

定义此值:

91.32
5.22
11.5
1.2
2.6

使用该方法,我们可以获得以下结果:

91.32
5.22
11.50
1.20
2.60

在线演示。


5

万一有人仍然需要帮助,以防万一。该解决方案非常适合我。

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

返回 String 具有所需输出的。


请在评论中包含您拒绝投票的理由,否则我们称之为恐吓。
Asher。M.ø

4

我同意选择的答案使用DecimalFormat---或替代方式BigDecimal

请先阅读下面的更新

但是,如果你想要圆的双重价值,并得到一个double数值结果,你可以使用org.apache.commons.math3.util.Precision.round(..)如上所述。实现使用BigDecimal,速度很慢,并且会产生垃圾。

DoubleRounder十进制4j库中的实用程序提供了一种类似但快速且无垃圾的方法:

 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

免责声明:我参与了October4j项目。

更新: 正如@iaforek指出,DoubleRounder有时会返回违反直觉的结果。原因是它执行数学上正确的舍入。例如,DoubleRounder.round(256.025d, 2)将四舍五入为256.02,因为表示为256.025d的双精度值比有理值256.025小一些,因此将四舍五入。

笔记:

  • 此行为与BigDecimal(double)构造函数的行为非常相似(但与valueOf(double)使用字符串构造函数的行为不同)。
  • 可以通过双舍入步骤规避该问题,首先要达到更高的精度,但是它很复杂,在此不做详细介绍

由于这些原因以及本文中上面提到的所有内容,我不建议使用DoubleRounder


您是否有指标显示您的解决方案与其他解决方案相比有多有效?
iaforek

我没有将其与其他解决方案进行比较,但是源代码中提供了一个jmh基准测试:github.com/tools4j/decimal4j/blob/master/src/jmh/java/org / ... 我确实在VM上运行了基准测试,结果可在此处以csv文件的形式获得:github.com/tools4j/decimal4j/wiki/Performance
marco

1
DoubleRounder在以下情况下失败:DoubleRounder.round(256.025d,2)-预期:256.03,实际:256.02或对于DoubleRounder.round(260.775d,2)-预期:260.78,实际:260.77。
iaforek

@iaforek:这是正确的,因为DoubleRounder执行数学上正确的舍入。但是,我承认这有点违反直觉,因此会相应更新我的答案。
marco

3

下面的代码段显示了如何显示n位数字。诀窍是将变量pp设置为1,后跟n个零。在下面的示例中,变量pp值具有5个零,因此将显示5位数字。

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();

3

如果您要DecimalFormat转换doubleString,则非常简单:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

RoundingMode根据需要的行为,有几个枚举值可供选择。


3

我来到这里只是想获得一个简单的答案,如何四舍五入一个数字。这是一个补充答案。

如何用Java四舍五入数字

最常见的情况是使用Math.round()

Math.round(3.7) // 4

数字四舍五入到最接近的整数。一个.5值向上舍入。如果您需要的舍入行为与此不同,则可以使用其他Math函数之一。请参阅下面的比较。

回合

如上所述,此舍入为最接近的整数。.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

细胞

任何十进制值都将四舍五入到下一个整数。到天花板。此方法返回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

int

这类似于舍入,即十进制值舍入到最接近的整数。但是,与不同round.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 ***

1
您只解决四舍五入到小数点后0的特殊情况。最初的问题更为笼统。
lukas84

3

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

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

如果myVal不小于1,且小数点后的小数点后为零,则失败。假设您有myVal = 9.00000000912385; 以上将返回9.0。我认为我们应该提供一种在myVal的所有情况下都适用的解决方案。不专门针对您指定的值。
tavalendo

@ user102859在您的示例中,9.0是正确的结果。我不知道这怎么会失败。
Craigo

2

DecimalFormat是输出的最佳方法,但我不喜欢它。我总是一直这样做,因为它返回double值。因此,我可以使用它,而不仅仅是输出。

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

要么

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

如果需要较大的小数位值,则可以改用BigDecimal。无论如何.0都很重要。如果没有它,则将0.33333d5的舍入返回0.33333,并且仅允许9位数字。第二个函数没有.0问题,则0.30000返回0.30000000000000004。


2

这是我的答案:

double num = 4.898979485566356;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(num));

System.out.println(num); // 4.89

1

因此,在阅读了大多数答案之后,我意识到其中大部分都不是精确的,实际上使用BigDecimal似乎是最佳选择,但是如果您不了解其RoundingMode工作原理,则不可避免地会失去精确性。当在项目中处理大量数字时,我想到了这一点,并认为它可以帮助其他难以四舍五入的数字。例如。

BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());

您可能希望将其1363.28作为输出,但是1363.27如果您不知道RoundingMode它在做什么,则最终将得到,这是不期望的。因此,查看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


0

其中dp =您想要的小数位,是双精度。

    double p = Math.pow(10d, dp);

    double result = Math.round(value * p)/p;

1
1.0value = 1.005和生产dp = 2。改用
Matthias Braun 2014年

没关系,马特,您的示例无效。因为无论如何不能用浮点双精度数表示1.005。它必须确实存储在1.005之上或之下,即在编译时存储为double:1.0049998(它不会以十进制的形式存储在已编译代码中,正如您希望读者相信的那样)目的是正确的,他将值存储为浮点数翻倍的,像您这样的边缘案例却微不足道。如果是这样,那么您将使用3dp,然后将其转换为十进制,然后执行十进制舍入函数,就像您发布的链接一样。
hamish

1
@hamish我看不到Matthias在哪里“会让读者相信”任何东西,因为该值被编译为十进制。不要在别人的嘴里放话。
user207421

0

请记住,String.format()和DecimalFormat使用默认的Locale生成字符串。因此,他们可以使用点或逗号作为整数和小数部分之间的分隔符来编写格式化数字。要确保四舍五入的String符合您想要的格式,请按以下方式使用java.text.NumberFormat:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

将以英语语言环境打印(无论您的语言环境是什么):0.99 123.57 123.00

该示例取自Farenda- 如何将double正确转换为String


0

通常,舍入是通过缩放来完成的: round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
    double c = 0.5 * EPSILON * num;
//  double p = Math.pow(10, precision); //slow
    double p = 1; while (precision--> 0) p *= 10;
    if (num < 0)
        p *= -1;
    return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.