Ale*_*ing 1209 java decimal rounding digits
我想要的是一种将double转换为使用half-up方法进行舍入的字符串的方法 - 即如果要舍入的小数为5,则它总是向上舍入到前一个数字.这是在大多数情况下舍入大多数人所期望的标准方法.
我也希望只显示有效数字 - 即不应该有任何尾随零.
我知道这样做的一种方法是使用该String.format
方法:
String.format("%.5g%n", 0.912385);
Run Code Online (Sandbox Code Playgroud)
收益:
0.91239
Run Code Online (Sandbox Code Playgroud)
这很好,但它总是显示5位小数的数字,即使它们不重要:
String.format("%.5g%n", 0.912300);
Run Code Online (Sandbox Code Playgroud)
收益:
0.91230
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用DecimalFormatter
:
DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);
Run Code Online (Sandbox Code Playgroud)
收益:
0.91238
Run Code Online (Sandbox Code Playgroud)
但是你可以看到这使用了半均匀舍入.也就是说,如果前一个数字是偶数,它将向下舍入.我想要的是这个:
0.912385 -> 0.91239
0.912300 -> 0.9123
Run Code Online (Sandbox Code Playgroud)
在Java中实现这一目标的最佳方法是什么?
cur*_*isk 721
使用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));
}
Run Code Online (Sandbox Code Playgroud)
给出输出:
12
123.1235
0.23
0.1
2341234.2125
Run Code Online (Sandbox Code Playgroud)
ast*_*ite 454
假设value
是a double
,你可以这样做:
(double)Math.round(value * 100000d) / 100000d
Run Code Online (Sandbox Code Playgroud)
这是5位精度.零的数量表示小数位数.
Met*_*002 186
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
Run Code Online (Sandbox Code Playgroud)
会得到你的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));
}
Run Code Online (Sandbox Code Playgroud)
Mil*_*ous 111
你也可以使用
DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);
Run Code Online (Sandbox Code Playgroud)
确保你有0的结尾.
use*_*421 82
正如其他人所说,正确的答案是使用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");
}
}
Run Code Online (Sandbox Code Playgroud)
该计划的输出:
10001 trials 9251 errors
Run Code Online (Sandbox Code Playgroud)
编辑:为了解决下面的一些评论,我使用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");
}
Run Code Online (Sandbox Code Playgroud)
结果:
10001 trials 4401 errors
Run Code Online (Sandbox Code Playgroud)
小智 80
假设你有
double d = 9232.129394d;
Run Code Online (Sandbox Code Playgroud)
您可以使用 BigDecimal
BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();
Run Code Online (Sandbox Code Playgroud)
或没有BigDecimal
d = Math.round(d*100)/100.0d;
Run Code Online (Sandbox Code Playgroud)
两种解决方案 d == 9232.13
Jib*_*ibW 57
您可以使用DecimalFormat类.
double d = 3.76628729;
DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal = Double.valueOf(newFormat.format(d));
Run Code Online (Sandbox Code Playgroud)
Ove*_*esh 37
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();
Run Code Online (Sandbox Code Playgroud)
Chr*_*ore 32
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
Run Code Online (Sandbox Code Playgroud)
小智 30
@Milhous:舍入的十进制格式非常好:
你也可以使用
Run Code Online (Sandbox Code Playgroud)DecimalFormat df = new DecimalFormat("#.00000"); df.format(0.912385);
确保你有0的结尾.
我想补充一点,这种方法非常擅长提供一种实际的数字舍入机制 - 不仅在视觉上,而且在处理时.
假设:您必须在GUI程序中实现舍入机制.要更改结果输出的准确度/精度,只需更改插入符号格式(即在括号内).以便:
DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);
Run Code Online (Sandbox Code Playgroud)
将作为输出返回: 0.912385
DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);
Run Code Online (Sandbox Code Playgroud)
将作为输出返回: 0.91239
DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);
Run Code Online (Sandbox Code Playgroud)
将作为输出返回: 0.9124
[编辑:如果插入符号格式如此("#0.############")并输入小数,例如3.1415926,为了参数的缘故,DecimalFormat不会产生任何垃圾(例如尾随零)并将返回:
3.1415926
..如果你倾向于那样.对于喜欢某些开发人员而言,它有点冗长 - 但是,它在处理过程中占用的内存很少,并且非常容易实现.
基本上,DecimalFormat的优点在于它同时处理字符串外观 - 以及舍入精度集的级别.Ergo:以一个代码实现的价格获得两个好处.;)
小智 18
您可以使用以下实用方法 -
public static double round(double valueToRound, int numberOfDecimalPlaces)
{
double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
double interestedInZeroDPs = valueToRound * multipicationFactor;
return Math.round(interestedInZeroDPs) / multipicationFactor;
}
Run Code Online (Sandbox Code Playgroud)
Mif*_*eet 18
以下是您希望结果为String时可以使用的内容摘要:
DecimalFormat #setRoundingMode():
DecimalFormat df = new DecimalFormat("#.#####");
df.setRoundingMode(RoundingMode.HALF_UP);
String str1 = df.format(0.912385)); // 0.91239
Run Code Online (Sandbox Code Playgroud)String str2 = new BigDecimal(0.912385)
.setScale(5, BigDecimal.ROUND_HALF_UP)
.toString();
Run Code Online (Sandbox Code Playgroud)这里有一个建议,如果你想要的话,你可以使用哪些库double
.我不建议将其用于字符串转换,因为double可能无法准确表示您想要的内容(请参阅此处):
精准的Apache Commons Math
double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
Run Code Online (Sandbox Code Playgroud)Colt的功能
double rounded = Functions.round(0.00001).apply(0.912385)
Run Code Online (Sandbox Code Playgroud)来自Weka的实用工具
double rounded = Utils.roundDouble(0.912385, 5)
Run Code Online (Sandbox Code Playgroud)MAb*_*am1 11
简洁的解决方案:
public static double round(double value, int precision) {
int scale = (int) Math.pow(10, precision);
return (double) Math.round(value * scale) / scale;
}
Run Code Online (Sandbox Code Playgroud)
另请参阅/sf/answers/1553079181/ 感谢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);
Run Code Online (Sandbox Code Playgroud)
参考:http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/
小智 7
如果你真的想要十进制数来计算(而不仅仅是输出),不要使用像double这样的基于二进制的浮点格式.
Use BigDecimal or any other decimal-based format.
Run Code Online (Sandbox Code Playgroud)
我确实使用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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于我没有找到关于这个主题的完整答案,我已经组建了一个应该正确处理这个问题的课程,并支持:
用法非常简单:
(为了这个例子,我使用的是自定义语言环境)
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
Run Code Online (Sandbox Code Playgroud)
这是班级:
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), "");
}
}
Run Code Online (Sandbox Code Playgroud)
我同意使用DecimalFormat
--- 或替代选择的答案BigDecimal
。
请先阅读下面的更新!
但是,如果您确实想要舍入双精度值并获得double
值结果,则可以org.apache.commons.math3.util.Precision.round(..)
如上所述使用。实现使用BigDecimal
, 很慢并且会产生垃圾。
DoubleRounder
decimal4j 库中的实用程序提供了一种类似但快速且无垃圾的方法:
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);
Run Code Online (Sandbox Code Playgroud)
会输出
0.667
0.666
1000.0
9.00800700601E10
Run Code Online (Sandbox Code Playgroud)
见 https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility
免责声明:我参与了 decimal4j 项目。
更新:
正如@iaforek 指出的,DoubleRounder 有时会返回违反直觉的结果。原因是它执行数学上正确的舍入。例如,DoubleRounder.round(256.025d, 2)
将向下舍入为 256.02,因为表示为 256.025d 的双精度值略小于有理值 256.025,因此将向下舍入。
笔记:
BigDecimal(double)
构造函数的行为非常相似(但与valueOf(double)
使用字符串构造函数的行为不同)。由于这些原因以及本文上面提到的所有内容,我不建议使用 DoubleRounder。
以防有人仍然需要帮助.这个解决方案非常适合我.
private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();
}
Run Code Online (Sandbox Code Playgroud)
返回 String
带有所需输出的a.
我来这里只是想得到一个关于如何对数字进行四舍五入的简单答案。这是提供这一点的补充答案。
最常见的情况是使用Math.round()
.
Math.round(3.7) // 4
Run Code Online (Sandbox Code Playgroud)
数字四舍五入到最接近的整数。值.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
Run Code Online (Sandbox Code Playgroud)
任何小数值都会向上舍入到下一个整数。它达到了天花板。该方法返回一个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
Run Code Online (Sandbox Code Playgroud)
任何小数值都会向下舍入到下一个整数。该方法返回一个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
Run Code Online (Sandbox Code Playgroud)
这与四舍五入类似,因为小数值四舍五入到最接近的整数。然而,与 不同的是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 ***
Run Code Online (Sandbox Code Playgroud)
为此,我们可以使用以下格式化程序:
DecimalFormat df = new DecimalFormat("#.00");
String resultado = df.format(valor)
Run Code Online (Sandbox Code Playgroud)
要么:
DecimalFormat df = new DecimalFormat("0.00"); :
Run Code Online (Sandbox Code Playgroud)
使用此方法总是获得两个小数:
private static String getTwoDecimals(double value){
DecimalFormat df = new DecimalFormat("0.00");
return df.format(value);
}
Run Code Online (Sandbox Code Playgroud)
定义此值:
91.32
5.22
11.5
1.2
2.6
Run Code Online (Sandbox Code Playgroud)
使用该方法,我们可以获得以下结果:
91.32
5.22
11.50
1.20
2.60
Run Code Online (Sandbox Code Playgroud)
因此,在阅读了大部分答案后,我意识到大多数答案都不会精确,事实上使用BigDecimal
似乎是最好的选择,但如果你不明白它是如何RoundingMode
工作的,你将不可避免地失去精度。我在项目中处理大数字时发现了这一点,并认为它可以帮助其他人在四舍五入数字时遇到困难。例如。
BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());
Run Code Online (Sandbox Code Playgroud)
您希望得到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();
}
Run Code Online (Sandbox Code Playgroud)
这最终会给我们预期的输出,即1363.28
。