Java 如何在没有不必要的十进制 0 的情况下很好地将浮点数格式化为字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/703396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to nicely format floating numbers to String without unnecessary decimal 0?
提问by Pyrolistical
An 64-bit double can represent integer +/- 253exactly
一个 64 位的 double 可以精确地表示整数 +/- 2 53
Given this fact I choose to use a double type as a single type for all my types, since my largest integer is unsigned 32-bit.
鉴于这一事实,我选择将 double 类型用作所有类型的单一类型,因为我的最大整数是 32 位无符号整数。
But now I have to print these pseudo integers, but the problem is they are also mixed in with actual doubles.
但是现在我必须打印这些伪整数,但问题是它们也与实际的双打混合在一起。
So how do I print these doubles nicely in Java?
那么如何在 Java 中很好地打印这些双打呢?
I have tried String.format("%f", value)
, which is close, except I get a lot of trailing zeros for small values.
我试过String.format("%f", value)
,这是接近的,除了我得到很多小值的尾随零。
Here's an example output of of %f
这是一个示例输出 %f
232.00000000 0.18000000000 1237875192.0 4.5800000000 0.00000000 1.23450000
What I want is:
我想要的是:
232 0.18 1237875192 4.58 0 1.2345
Sure I can write a function to trim those zeros, but that's lot of performance loss due to String manipulation. Can I do better with another format code?
当然我可以编写一个函数来修剪这些零,但是由于字符串操作,性能损失很大。我可以用另一种格式代码做得更好吗?
EDIT
编辑
The answers by Tom E. and Jeremy S. are unacceptable as they both arbitrarily rounds to 2 decimal places. Please understand the problem before answering.
Tom E. 和 Jeremy S. 的答案是不可接受的,因为他们都任意四舍五入到小数点后两位。回答前请先了解问题。
EDIT 2
编辑 2
Please note that String.format(format, args...)
is locale-dependent(see answers below).
请注意,String.format(format, args...)
是区域设置相关的(见下面的答案)。
采纳答案by JasonD
If the idea is to print integers stored as doubles as if they are integers, and otherwise print the doubles with the minimum necessary precision:
如果想法是打印存储为双精度数的整数,就好像它们是整数一样,否则以最低必要精度打印双精度数:
public static String fmt(double d)
{
if(d == (long) d)
return String.format("%d",(long)d);
else
return String.format("%s",d);
}
Produces:
产生:
232
0.18
1237875192
4.58
0
1.2345
And does not rely on string manipulation.
并且不依赖字符串操作。
回答by Pyrolistical
Naw, never mind.
不,没关系。
Performance loss due to String manipulation is zero.
由于字符串操作导致的性能损失为零。
And here's the code to trim the end after %f
这是修剪结束后的代码 %f
private static String trimTrailingZeros(String number) {
if(!number.contains(".")) {
return number;
}
return number.replaceAll("\.?0*$", "");
}
回答by Jeremy Slade
String.format("%.2f", value) ;
回答by Tom Esterez
new DecimalFormat("#.##").format(1.199); //"1.2"
As pointed in the comments, this is not the right answer to the original question.
That said, it is a very useful way to format numbers without unnecessary trailing zeros.
正如评论中所指出的,这不是原始问题的正确答案。
也就是说,这是一种非常有用的方法来格式化数字而没有不必要的尾随零。
回答by Kadi
String s = "1.210000";
while (s.endsWith("0")){
s = (s.substring(0, s.length() - 1));
}
This will make the string to drop the tailing 0-s.
这将使字符串删除拖尾 0-s。
回答by sethu
I know this is a really old thread.. But I think the best way to do this is as below:
我知道这是一个非常古老的线程..但我认为最好的方法如下:
public class Test {
public static void main(String args[]){
System.out.println(String.format("%s something",new Double(3.456)));
System.out.println(String.format("%s something",new Double(3.456234523452)));
System.out.println(String.format("%s something",new Double(3.45)));
System.out.println(String.format("%s something",new Double(3)));
}
}
Output:
输出:
3.456 something
3.456234523452 something
3.45 something
3.0 something
The only issue is the last one where .0 doesn't get removed. But if you are able to live with that then this works best. %.2f will round it to the last 2 decimal digits. So will DecimalFormat. If you need all the decimal places but not the trailing zeros then this works best.
唯一的问题是最后一个没有删除 .0 的问题。但如果你能忍受它,那么这最有效。%.2f 会将其四舍五入到最后 2 位十进制数字。DecimalFormat 也是如此。如果您需要所有小数位而不是尾随零,那么这种方法效果最好。
回答by Hiep
I made a DoubleFormatter
to efficiently convert a great numbers of double values to a nice/presentable String:
我做了一个DoubleFormatter
有效地将大量双精度值转换为一个漂亮/可展示的字符串:
double horribleNumber = 3598945.141658554548844;
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
- If the integer part of V has more than MaxInteger => display V in scientist format (1.2345e+30) otherwise display in normal format 124.45678.
- the MaxDecimal decide numbers of decimal digits (trim with banker's rounding)
- 如果 V 的整数部分大于 MaxInteger => 以科学家格式 (1.2345e+30) 显示 V,否则以正常格式 124.45678 显示。
- MaxDecimal 决定小数位数(用银行家的四舍五入修整)
Here the code:
这里的代码:
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
/**
* Convert a double to a beautiful String (US-local):
*
* double horribleNumber = 3598945.141658554548844;
* DoubleFormatter df = new DoubleFormatter(4,6);
* String beautyDisplay = df.format(horribleNumber);
* String beautyLabel = df.formatHtml(horribleNumber);
*
* Manipulate 3 instances of NumberFormat to efficiently format a great number of double values.
* (avoid to create an object NumberFormat each call of format()).
*
* 3 instances of NumberFormat will be reused to format a value v:
*
* if v < EXP_DOWN, uses nfBelow
* if EXP_DOWN <= v <= EXP_UP, uses nfNormal
* if EXP_UP < v, uses nfAbove
*
* nfBelow, nfNormal and nfAbove will be generated base on the precision_ parameter.
*
* @author: DUONG Phu-Hiep
*/
public class DoubleFormatter
{
private static final double EXP_DOWN = 1.e-3;
private double EXP_UP; // always = 10^maxInteger
private int maxInteger_;
private int maxFraction_;
private NumberFormat nfBelow_;
private NumberFormat nfNormal_;
private NumberFormat nfAbove_;
private enum NumberFormatKind {Below, Normal, Above}
public DoubleFormatter(int maxInteger, int maxFraction){
setPrecision(maxInteger, maxFraction);
}
public void setPrecision(int maxInteger, int maxFraction){
Preconditions.checkArgument(maxFraction>=0);
Preconditions.checkArgument(maxInteger>0 && maxInteger<17);
if (maxFraction == maxFraction_ && maxInteger_ == maxInteger) {
return;
}
maxFraction_ = maxFraction;
maxInteger_ = maxInteger;
EXP_UP = Math.pow(10, maxInteger);
nfBelow_ = createNumberFormat(NumberFormatKind.Below);
nfNormal_ = createNumberFormat(NumberFormatKind.Normal);
nfAbove_ = createNumberFormat(NumberFormatKind.Above);
}
private NumberFormat createNumberFormat(NumberFormatKind kind) {
final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);
NumberFormat f = NumberFormat.getInstance(Locale.US);
//Apply banker's rounding: this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
f.setRoundingMode(RoundingMode.HALF_EVEN);
if (f instanceof DecimalFormat) {
DecimalFormat df = (DecimalFormat) f;
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
//set group separator to space instead of comma
//dfs.setGroupingSeparator(' ');
//set Exponent symbol to minus 'e' instead of 'E'
if (kind == NumberFormatKind.Above) {
dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
} else {
dfs.setExponentSeparator("e");
}
df.setDecimalFormatSymbols(dfs);
//use exponent format if v is out side of [EXP_DOWN,EXP_UP]
if (kind == NumberFormatKind.Normal) {
if (maxFraction_ == 0) {
df.applyPattern("#,##0");
} else {
df.applyPattern("#,##0."+sharpByPrecision);
}
} else {
if (maxFraction_ == 0) {
df.applyPattern("0E0");
} else {
df.applyPattern("0."+sharpByPrecision+"E0");
}
}
}
return f;
}
public String format(double v) {
if (Double.isNaN(v)) {
return "-";
}
if (v==0) {
return "0";
}
final double absv = Math.abs(v);
if (absv<EXP_DOWN) {
return nfBelow_.format(v);
}
if (absv>EXP_UP) {
return nfAbove_.format(v);
}
return nfNormal_.format(v);
}
/**
* format and higlight the important part (integer part & exponent part)
*/
public String formatHtml(double v) {
if (Double.isNaN(v)) {
return "-";
}
return htmlize(format(v));
}
/**
* This is the base alogrithm: create a instance of NumberFormat for the value, then format it. It should
* not be used to format a great numbers of value
*
* We will never use this methode, it is here only to understanding the Algo principal:
*
* format v to string. precision_ is numbers of digits after decimal.
* if EXP_DOWN <= abs(v) <= EXP_UP, display the normal format: 124.45678
* otherwise display scientist format with: 1.2345e+30
*
* pre-condition: precision >= 1
*/
@Deprecated
public String formatInefficient(double v) {
final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);
final double absv = Math.abs(v);
NumberFormat f = NumberFormat.getInstance(Locale.US);
//Apply banker's rounding: this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
f.setRoundingMode(RoundingMode.HALF_EVEN);
if (f instanceof DecimalFormat) {
DecimalFormat df = (DecimalFormat) f;
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
//set group separator to space instead of comma
dfs.setGroupingSeparator(' ');
//set Exponent symbol to minus 'e' instead of 'E'
if (absv>EXP_UP) {
dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
} else {
dfs.setExponentSeparator("e");
}
df.setDecimalFormatSymbols(dfs);
//use exponent format if v is out side of [EXP_DOWN,EXP_UP]
if (absv<EXP_DOWN || absv>EXP_UP) {
df.applyPattern("0."+sharpByPrecision+"E0");
} else {
df.applyPattern("#,##0."+sharpByPrecision);
}
}
return f.format(v);
}
/**
* Convert "3.1416e+12" to "<b>3</b>.1416e<b>+12</b>"
* It is a html format of a number which highlight the integer and exponent part
*/
private static String htmlize(String s) {
StringBuilder resu = new StringBuilder("<b>");
int p1 = s.indexOf('.');
if (p1>0) {
resu.append(s.substring(0, p1));
resu.append("</b>");
} else {
p1 = 0;
}
int p2 = s.lastIndexOf('e');
if (p2>0) {
resu.append(s.substring(p1, p2));
resu.append("<b>");
resu.append(s.substring(p2, s.length()));
resu.append("</b>");
} else {
resu.append(s.substring(p1, s.length()));
if (p1==0){
resu.append("</b>");
}
}
return resu.toString();
}
}
Note: I used 2 functions from GUAVA library. If you don't use GUAVA, code it yourself:
注意:我使用了 GUAVA 库中的 2 个函数。如果不使用 GUAVA,请自行编码:
/**
* Equivalent to Strings.repeat("#", n) of the Guava library:
*/
private static String createSharp(int n) {
StringBuilder sb = new StringBuilder();
for (int i=0;i<n;i++) {
sb.append('#');
}
return sb.toString();
}
回答by kamal
String s = String.valueof("your int variable");
while (g.endsWith("0") && g.contains(".")) {
g = g.substring(0, g.length() - 1);
if (g.endsWith("."))
{
g = g.substring(0, g.length() - 1);
}
}
回答by android developer
Here are two ways to achieve it. First, the shorter (and probably better) way:
这是实现它的两种方法。首先,更短(可能更好)的方式:
public static String formatFloatToString(final float f)
{
final int i=(int)f;
if(f==i)
return Integer.toString(i);
return Float.toString(f);
}
And here's the longer and probably worse way:
这是更长且可能更糟的方式:
public static String formatFloatToString(final float f)
{
final String s=Float.toString(f);
int dotPos=-1;
for(int i=0;i<s.length();++i)
if(s.charAt(i)=='.')
{
dotPos=i;
break;
}
if(dotPos==-1)
return s;
int end=dotPos;
for(int i=dotPos+1;i<s.length();++i)
{
final char c=s.charAt(i);
if(c!='0')
end=i+1;
}
final String result=s.substring(0,end);
return result;
}
回答by Martin Klosi
Here is an answer that actually works (combination of different answers here)
这是一个实际有效的答案(此处是不同答案的组合)
public static String removeTrailingZeros(double f)
{
if(f == (int)f) {
return String.format("%d", (int)f);
}
return String.format("%f", f).replaceAll("0*$", "");
}