如何很好地格式化浮动数字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.printlnFloatingDecimal.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,789123,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 
  1. 0为最小位数
  2. 呈现#个数字

这是一个真正有效的答案(在这里结合不同的答案)

 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在声纳报告中给我违规

使用DecimalFormatsetMinimumFractionDigits(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。