当前位置:  开发笔记 > 编程语言 > 正文

如何在Java中将数字舍入到n个小数位

如何解决《如何在Java中将数字舍入到n个小数位》经验,为你挑选了19个好方法。

我想要的是一种将double转换为使用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> curtisk..:

使用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


这可能是目前为止提出的最佳解决方案.我第一次看到DecimalFormat类时没有发现这个工具的原因是它只是在Java 1.6中引入的.不幸的是,我只能使用1.5,但知道将来会很有用.
请小心,因为DecimalFormat依赖于您当前的本地配置,您可能无法获得一个点作为分隔符.我个人更喜欢下面的Asterite答案

2> asterite..:

假设value是a double,你可以这样做:

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

这是5位精度.零的数量表示小数位数.


超过90%的情况下,该技术失败.-1.
更新:我刚刚确认这样做比使用DecimalFormat更快.我使用DecimalFormat 200次和这个方法循环.DecimalFormat花了14ms来完成200个循环,这个方法花了不到1ms.我怀疑,这更快.如果您按时钟周期获得报酬,那么您应该这样做.我很惊讶克里斯·库德莫尔甚至会说出他所说的诚实.分配对象总是比使用静态方法(Math.round()而不是decimalFormat.format())更昂贵.
使用此方法(或任何浮点舍入)时要非常小心.它没有像265.335这样简单的东西.265.335*100(2位精度)的中间结果为26533.499999999996.这意味着它将向下舍入到265.33.从浮点数转换为实数十进制数时,存在固有的问题.请参阅http://stackoverflow.com/a/12684082/144578上的EJP答案
实际上,这失败了:`Math.round(0.1*Math.pow(10,20))/ Math.pow(10,20)== 0.09223372036854775`.
@SebastiaanvandenBroek:哇,我从来不知道得到错误答案很容易.但是,如果使用非精确数字,则必须认识到任何值*都不精确*.`265.335`实际上意味着`265.335 + = tolerance`,其中容差取决于先前的操作和输入值的范围.我们不知道真实的,准确的价值.**在边缘值,***答案可以说是正确的.**如果我们需要准确,我们不应该双倍工作.这里的`fail`不是转换回double.它在OP中认为他可以依赖于传入的"265.335"就是这样.
@ RobertTupelo-Schneck这不起作用,因为double只有大约14个小数位的精度(不管怎么说都不准确)
@AndiJay:"如果你按时钟周期获得报酬,这就是你应该做的事情" - 我不同意.如果按时钟周期付费,我会使用`DecimalFormat`使我的代码尽可能慢:它需要的时钟周期越多,对我来说就越多钱;)
@Andi Jay我不是说你错了或者是对的,但你不能说在一台机器上进行隔离测试证实了你的理论.
我认为做乘法会比除法快,所以`(double)Math.round(value*100000)*0.000001`
@ RobertTupelo-Schneck它工作到10 ^ 19;)
这也失败,非常小的值:`(double)Math.round(1.005*100)/ 100 == 1.0`不要使用它!
1.005 == 1.00和265.335 == 265.33 - >那又怎么样!没事.有浮点转换进入循环,有浮点转换进入除法,浮点转换进入100.0你只需要接受浮点是浮点......它是近似的!小数.浮点双倍快!确定你失去了一些精确度,但谁在乎...它的超级快速!
对于速度,我已经在$ 5.00变为500之前转换为int.超快速数学,总是四舍五入到2个小数点,并且显示只需要除以100.

3> MetroidFan20..:
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

会得到你的BigDecimal.要从中获取字符串,只需调用该方法,或者使用Java 5+ BigDecimaltoString方法调用toPlainString普通格式字符串.

示例程序:

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


这是我的首选解决方案.更短:BigDecimal.valueOf(doubleVar).setScale(yourScaleHere,BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf(double val)实际上在引擎盖下调用Double.toString();)
@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`.
@ToolmakerSteve那是因为使用带有double的新BigDecimal直接获取double值并尝试使用它来创建BigDecimal,而使用BigDecimal.valueOf或tostring表单在转换之前首先将其解析为字符串(更精确的表示) .
尼斯.不要偷工减料并使用`new BigDecimal(doubleVar)`,因为你可能会遇到浮点舍入问题

4> Milhous..:

你也可以使用

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

确保你有0的结尾.


我相信这个问题的目标之一是"应该**不应该是任何尾随的零".
对于这个问题,op不想要零,但这正是我想要的.如果你有一个包含3个小数位的数字列表,你希望它们都具有相同的数字,即使它是0.

5> user207421..:

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


您所做的只是反驳浮动并不完全代表许多十进制值,我希望大家都能理解.不是圆角确实会导致问题.正如您承认的那样,数字仍按预期打印.
诀窍在于,在所有9251错误中,打印结果仍然正确.
你的测试被打破,取出(),测试在94%的时间内失败.http://ideone.com/1y62CY打印'100试验94错误'你应该从通过的测试开始,并表明引入舍入打破了测试.
@DidierL我并不感到惊讶.我非常幸运地将"数值方法"作为我的第一个计算过程,并在开始时介绍浮点能够做什么和不能做什么.大多数程序员对此都很模糊.
反驳,在这里驳斥.将Math.round用于此范围的`double`,因为没有错误http://ideone.com/BVCHh3
虽然原点是站立的(浮点的截断与十进制舍入不同),但这个反例对我来说有点怀疑.请注意,我们知道double 0.25正好等于1/4,但`((0.25%0.01)== 0.0)`的计算结果为false.
有一个解释:(d%0.01)会增加结果的不准确性并影响舍入操作的精度.因此,它只能证明92%的时间模数运算不精确.我有一个反例:`code double d1,d2; d1 = 10.0100000000000000000123D; d2 = 10.0100000D; System.out.println(Math.round(d1*100)/ 100D == d2); // true double d3 = 0.0000090000000000000000000789D; System.out.println(Math.round(d3*100)/ 100D == 0.0D); //真的BTW,我不是在提倡这种技术.它容易出现溢出.
我会将测试从`if((d%0.01)!= 0.0)`更改为`if(BigDecimal.valueOf(d).compareTo(new BigDecimal(d))!= 0)`,这表示错误率甚至高于最初的估计(并且随着规模的增加而增加,达到99.9%,等级为5)

6> 小智..:

假设你有

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


第一个解决方案是正确的:第二个解决方案不起作用.有关证明,请参见[此处](http://stackoverflow.com/a/7593617/207421).
我认为这是Java 1.5用户(及以下)的最佳解决方案.一个评论,不要使用HALF_EVEN舍入模式,因为它具有奇数和偶数的差异行为(例如,2.5轮到2,而5.5轮到6),除非这是你想要的.

7> JibW..:

您可以使用DecimalFormat类.

double d = 3.76628729;

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



8> Ovesh..:

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



9> Chris Cudmor..:
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;


是的,这正是math.round对正数所做的,但是你用负数做过这个吗?人们在其他解决方案中使用math.round也涵盖负数的情况.

10> 小智..:

@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的优点在于它同时处理字符串外观 - 以及舍入精度集的级别.Ergo:以一个代码实现的价格获得两个好处.;)


如果你真的想要十进制数来计算(而不仅仅是输出),**不要使用基于二进制的浮点格式**,如`double`.使用BigDecimal或任何其他基于十进制的格式.

11> 小智..:

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

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


不起作用,请参见[此处](http://stackoverflow.com/a/7593617/207421)进行校对.

12> Mifeet..:

以下是您希望结果为String时可以使用的内容摘要:

    DecimalFormat #setRoundingMode():

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

    BigDecimal的#setScale()

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

这里有一个建议,如果你想要的话,你可以使用哪些库double.我不建议将其用于字符串转换,因为double可能无法准确表示您想要的内容(请参阅此处):

    精准的Apache Commons Math

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
    

    Colt的功能

    double rounded = Functions.round(0.00001).apply(0.912385)
    

    来自Weka的实用工具

    double rounded = Utils.roundDouble(0.912385, 5)
    



13> MAbraham1..:

简洁的解决方案:

   public static double round(double value, int precision) {
      int scale = (int) Math.pow(10, precision);
      return (double) Math.round(value * scale) / scale;
  }

另请参阅/sf/ask/17360801/ 感谢jpdymond提供此功能.



14> Easwaramoort..:

您可以使用BigDecimal

BigDecimal value = new BigDecimal("2.3");
value = value.setScale(0, RoundingMode.UP);
BigDecimal value1 = new BigDecimal("-2.3");
value1 = value1.setScale(0, RoundingMode.UP);
System.out.println(value + "n" + value1);

参考:http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/



15> 小智..:

如果你真的想要十进制数来计算(而不仅仅是输出),不要使用像double这样的基于二进制的浮点格式.

Use BigDecimal or any other decimal-based format.

我确实使用BigDecimal进行计算,但请记住它取决于您正在处理的数字的大小.在我的大多数实现中,我发现从double或者整数到Long的解析足以进行非常大量的计算.

事实上,我最近使用parsed-to-Long在GUI中获得与##################一样大的数字的准确表示(而不是十六进制结果) ###############字符(作为示例).



16> Li Ying..:

试试这个: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数学图书馆主页是: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;
        }
    }
}



17> Andrei Lupsa..:

由于我没有找到关于这个主题的完整答案,我已经组建了一个应该正确处理这个问题的课程,并支持:

格式化:使用一定数量的小数位轻松地将double 格式化为字符串

解析:将格式化的值解析为double

区域设置:使用默认区域设置格式化和解析

指数表示法:在某个阈值后开始使用指数表示法

用法非常简单:

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

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), "");
    }

}



18> 小智..:

以防有人仍然需要帮助.这个解决方案非常适合我.

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

}

返回 String 带有所需输出的a.



19> Jorgesys..:

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

 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

在线演示。

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有