我想要的是一种将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中实现这一目标的最佳方法是什么?
使用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
假设value
是a double
,你可以这样做:
(double)Math.round(value * 100000d) / 100000d
这是5位精度.零的数量表示小数位数.
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
会得到你的BigDecimal
.要从中获取字符串,只需调用该方法,或者使用Java 5+ BigDecimal
的toString
方法调用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)); }
你也可以使用
DecimalFormat df = new DecimalFormat("#.00000"); df.format(0.912385);
确保你有0的结尾.
正如其他人所说,正确的答案是使用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
假设你有
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
您可以使用DecimalFormat类.
double d = 3.76628729; DecimalFormat newFormat = new DecimalFormat("#.##"); double twoDecimal = Double.valueOf(newFormat.format(d));
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();
double myNum = .912385; int precision = 10000; //keep 4 digits myNum= Math.floor(myNum * precision +.5)/precision;
@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:以一个代码实现的价格获得两个好处.;)
您可以使用以下实用方法 -
public static double round(double valueToRound, int numberOfDecimalPlaces) { double multipicationFactor = Math.pow(10, numberOfDecimalPlaces); double interestedInZeroDPs = valueToRound * multipicationFactor; return Math.round(interestedInZeroDPs) / multipicationFactor; }
以下是您希望结果为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)
简洁的解决方案:
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提供此功能.
您可以使用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/
如果你真的想要十进制数来计算(而不仅仅是输出),不要使用像double这样的基于二进制的浮点格式.
Use BigDecimal or any other decimal-based format.
我确实使用BigDecimal进行计算,但请记住它取决于您正在处理的数字的大小.在我的大多数实现中,我发现从double或者整数到Long的解析足以进行非常大量的计算.
事实上,我最近使用parsed-to-Long在GUI中获得与##################一样大的数字的准确表示(而不是十六进制结果) ###############字符(作为示例).
试试这个: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; } } }
由于我没有找到关于这个主题的完整答案,我已经组建了一个应该正确处理这个问题的课程,并支持:
格式化:使用一定数量的小数位轻松地将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), ""); } }
以防有人仍然需要帮助.这个解决方案非常适合我.
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.
为此,我们可以使用以下格式化程序:
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
在线演示。