如何很好地格式化浮动数字string没有不必要的十进制0?
一个64位的double可以精确地表示整数+/- 2 53
鉴于这个事实,我select使用双types作为我所有types的单一types,因为我最大的整数是无符号的32位。
但现在我必须打印这些伪整数,但问题是他们也混入了实际的双打。
那么如何在Java中很好地打印这些双打呢?
我试过String.format("%f", value)
,这是接近的,除了我得到很多小值的尾随零。
以下是%f
输出示例
232.00000000 0.18000000000 1237875192.0 4.5800000000 0.00000000 1.23450000
我想要的是:
232 0.18 1237875192 4.58 0 1.2345
当然,我可以写一个函数来修剪这些零,但是由于string操作,性能会有很大的损失。 我可以用另一个格式代码更好吗?
编辑
Tom E.和Jeremy S.的答案是无法接受的,因为它们都是任意取整到小数点后两位。 请在回答之前了解问题。
编辑2
请注意, String.format(format, args...)
是语言环境相关的 (请参阅下面的答案)。
如果想法是将整数作为双精度来存储,就像是整数一样,否则用最小的必要精度打印双精度:
public static String fmt(double d) { if(d == (long) d) return String.format("%d",(long)d); else return String.format("%s",d); }
生产:
232 0.18 1237875192 4.58 0 1.2345
并不依赖于string操作。
new DecimalFormat("#.##").format(1.199); //"1.2"
正如评论所指出的,这不是对原始问题的正确答案。
这就是说,这是一个非常有用的方式来格式化数字,而不必要的尾随零。
String.format("%.2f", value) ;
简而言之:
如果你想摆脱尾随零和区域设置问题,那么你应该使用:
double myValue = 0.00000021d; DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS System.out.println(df.format(myValue)); //output: 0.00000021
说明:
为什么其他答案不适合我:
-
如果double小于10 ^ -3或大于等于10 ^ 7,则
Double.toString()
或System.out.println
或FloatingDecimal.toJavaFormatString
使用科学记数法double myValue = 0.00000021d; String.format("%s", myvalue); //output: 2.1E-7
-
通过使用
%f
,默认的小数精度是6,否则你可以硬编码它,但是如果你有小的小数,会导致额外的零。 例如:double myValue = 0.00000021d; String.format("%.12f", myvalue); //output: 0.000000210000
-
通过使用
setMaximumFractionDigits(0);
或%.0f
你删除任何小数的精度,这是很好的整数/长整数,但不是双double myValue = 0.00000021d; System.out.println(String.format("%.0f", myvalue)); //output: 0 DecimalFormat df = new DecimalFormat("0"); System.out.println(df.format(myValue)); //output: 0
-
通过使用DecimalFormat,你是本地的依赖。 在法语区域,小数点分隔符是一个逗号,而不是一个点:
double myValue = 0.00000021d; DecimalFormat df = new DecimalFormat("0"); df.setMaximumFractionDigits(340); System.out.println(df.format(myvalue));//output: 0,00000021
使用英语语言环境可确保您获得小数点分隔符,无论您的程序在哪里运行
为什么使用340,然后为setMaximumFractionDigits
?
两个原因:
-
setMaximumFractionDigits
接受一个整数,但是它的实现有一个DecimalFormat.DOUBLE_FRACTION_DIGITS
允许的最大数字,它等于340 -
Double.MIN_VALUE = 4.9E-324
所以340数字,你肯定不会围绕你的双精度和宽松
在我的机器上,下面的函数比JasonD的答案快大约7倍,因为它避免了String.format
:
public static String prettyPrint(double d) { int i = (int) d; return d == i ? String.valueOf(i) : String.valueOf(d); }
为什么不:
if (d % 1.0 != 0) return String.format("%s", d); else return String.format("%.0f",d);
这应该与Double支持的极端值一起工作。 产量:
0.12 12 12.144252 0
我的2美分:
if(n % 1 == 0) { return String.format(Locale.US, "%.0f", n)); } else { return String.format(Locale.US, "%.1f", n)); }
呃,没关系。
由于string操作造成的性能损失为零。
这里是在%f
之后修改结尾的代码
private static String trimTrailingZeros(String number) { if(!number.contains(".")) { return number; } return number.replaceAll("\\.?0*$", ""); }
我做了一个DoubleFormatter
高效地将大量的double值转换为一个漂亮的/可呈现的string:
double horribleNumber = 3598945.141658554548844; DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal String beautyDisplay = df.format(horribleNumber);
- 如果V的整数部分超过MaxInteger =>科学家格式显示V(1.2345e + 30),否则以正常格式124.45678显示。
- MaxDecimal决定十进制数字的数目(用银行家的四舍五入修整)
这里代码:
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(); } }
注意:我使用了GUAVA库中的两个函数。 如果您不使用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(); }
请注意, String.format(format, args...)
是依赖于语言环境的,因为它使用用户的默认语言环境进行格式化,也就是说可能带有逗号和空格,如123 456,789或123,456.789 ,这可能不是你期望。
您可能更喜欢使用String.format((Locale)null, format, args...)
。
例如,
double f = 123456.789d; System.out.println(String.format(Locale.FRANCE,"%f",f)); System.out.println(String.format(Locale.GERMANY,"%f",f)); System.out.println(String.format(Locale.US,"%f",f));
版画
123456,789000 123456,789000 123456.789000
这是什么String.format(format, args...)
在不同的国家做。
编辑好的,因为有关于手续的讨论:
res += stripFpZeroes(String.format((Locale) null, (nDigits!=0 ? "%."+nDigits+"f" : "%f"), value)); ... protected static String stripFpZeroes(String fpnumber) { int n = fpnumber.indexOf('.'); if (n == -1) { return fpnumber; } if (n < 2) { n = 2; } String s = fpnumber; while (s.length() > n && s.endsWith("0")) { s = s.substring(0, s.length()-1); } return s; }
这个人会很好的完成工作,我知道这个话题已经老了,但是我一直在为同样的问题而努力,直到我来到这里。 我希望有人觉得有用。
public static String removeZero(double number) { DecimalFormat format = new DecimalFormat("#.###########"); return format.format(number); }
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); } }
迟到的答案,但…
你说你select用双重types存储你的号码。 我认为这可能是问题的根源,因为它迫使你将整数存储为双精度(因此失去了关于值本质的初始信息)。 将数字存储在Number类(Double和Integer的超类)的实例中,并依赖多态性来确定每个数字的正确格式呢?
我知道重构你的代码的整个部分可能是不可接受的,但它可以产生所需的输出没有额外的代码/转换/parsing。
例:
import java.util.ArrayList; import java.util.List; public class UseMixedNumbers { public static void main(String[] args) { List<Number> listNumbers = new ArrayList<Number>(); listNumbers.add(232); listNumbers.add(0.18); listNumbers.add(1237875192); listNumbers.add(4.58); listNumbers.add(0); listNumbers.add(1.2345); for (Number number : listNumbers) { System.out.println(number); } } }
将产生以下输出:
232 0.18 1237875192 4.58 0 1.2345
if (d == Math.floor(d)) { return String.format("%.0f", d); } else { return Double.toString(d); }
new DecimalFormat("00.#").format(20.236) //out =20.2 new DecimalFormat("00.#").format(2.236) //out =02.2
- 0为最小位数
- 呈现#个数字
这是一个真正有效的答案(在这里结合不同的答案)
public static String removeTrailingZeros(double f) { if(f == (int)f) { return String.format("%d", (int)f); } return String.format("%f", f).replaceAll("0*$", ""); }
以下是两种实现方法。 首先,更短(也许更好)的方式:
public static String formatFloatToString(final float f) { final int i=(int)f; if(f==i) return Integer.toString(i); return Float.toString(f); }
这是更长,也许更糟糕的方式:
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; }
public static String fmt(double d) { String val = Double.toString(d); String[] valArray = val.split("\\."); long valLong = 0; if(valArray.length == 2){ valLong = Long.parseLong(valArray[1]); } if (valLong == 0) return String.format("%d", (long) d); else return String.format("%s", d); }
我不得不使用这个原因d == (long)d
在声纳报告中给我违规
使用DecimalFormat
和setMinimumFractionDigits(0)
我知道这是一个非常古老的线程..但我认为做到这一点的最佳方法如下:
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))); } }
输出:
3.456 something 3.456234523452 something 3.45 something 3.0 something
唯一的问题是最后一个.0没有被删除。 但是,如果你能够接受这一点,那么这是最好的。 %.2f将其舍入到最后2位十进制数字。 所以将DecimalFormat。 如果你需要所有的小数位,但不是尾随零,那么这个效果最好。
String s = "1.210000"; while (s.endsWith("0")){ s = (s.substring(0, s.length() - 1)); }
这将使string删除拖尾的0-s。