Related
I have to implement an interface and try it out in two different classes one with Array and one with List. In these classes we have to add different methods as seen in the interface ->
public interface IStatistics {
public void addSample(String name, GregorianCalendar date, double value);
public double getMeanTemperature(int month);
public double getMedianTemperature(int month);
public double getVariance(int month);
public double getStandardDeviation(int month);
public void printSamples(int month);
}
I've already done most of it but it should only use the values from a specific month which doesn't work in both methods. In this exercise we get a certain value every day for one year(name, date, temperature). I don't know how to get it to only use the values from one month(which doesn't work in both classes) In the List I already tried it out but it doesn't work at all.
#Override
public double getMeanTemperature(int month) {
MeasurementValue m = this.head;
double result = 0;
while(m.date.get(GregorianCalendar.MONTH)==month){
while(m!=null){
result = result + m.temp;
m=m.getNext();
}
result = result/this.num;
}
return result;
}
And in the class with Array I don't even know how to start.
#Override
public double getMeanTemperature(int month) {
double sum = 0.0;
for(double a : values)
sum += a;
return sum/numValues;
}
Also it doesn't print out the dates correctly. Maybe anyone knows a solution for that.
import java.util.GregorianCalendar;
import kwm.statistics.IStatistics;
import kwm.statistics.MeasurementArray;
import kwm.statistics.MeasurementList;
public class TestStatistics {
public static void main(String[] args) {
GregorianCalendar date = new GregorianCalendar(2017,05,12);
GregorianCalendar date1 = new GregorianCalendar(2017,05,02);
GregorianCalendar date2 = new GregorianCalendar(2017,05,22);
GregorianCalendar date3 = new GregorianCalendar(2017,06,06);
GregorianCalendar date4 = new GregorianCalendar(2017,05,18);
// ***TESTLIST***
IStatistics statistics1 = new MeasurementList();
statistics1.addSample("Lisa", date, 12);
statistics1.addSample("Lisa", date1, 17);
statistics1.addSample("Lisa", date2, 14);
statistics1.printSamples(5);
// ***TESTARRAY***
IStatistics statistics2 = new MeasurementArray();
statistics2.addSample("Lisa", date, 12);
statistics2.addSample("Lisa", date1, 5);
statistics2.addSample("Lisa", date2, 40);
statistics2.addSample("Lisa", date3, 31);
statistics2.addSample("Lisa", date4, 23);
statistics2.printSamples(5);
}
}
That's the code for the output in MeasurementValue
public void output(){
GregorianCalendar date = this.date;
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
System.out.print(sdf.format(date.getTime()));
}
And the code for the output in MeasurementList.
public void printSamples(int month) {
MeasurementValue m = this.head;
// while(m.date.get(GregorianCalendar.MONTH)==month){
while(m != null){
if(month>=0 && 11>=month){
System.out.println("Name: "+m.getName());
System.out.println("Value: "+m.getTemp());
System.out.print("Date: ");m.output();
System.out.print("\n");
System.out.print("*****************\n\n");
m = m.getNext();
}
}
// }
System.out.print("\n");
}
Thanks for any help!
For the linked list version, try something like this:
#Override
public double getMeanTemperature(int month) {
double total = 0d;
int num = 0;
for (MeasurementValue m = this.head; m != null; m = m.getNext()) {
if (m.date.get(Calendar.MONTH) == month) {
total += m.temp;
num++;
}
}
if (num == 0) {
// Avoid division by zero
return 0d;
}
return total / (double) num;
}
Same principle for the array but you iterate differently (and you need to store the months as well).
Silly question but can't find the answer.
double divider = 1000;
List<Long> listLong = new ArrayList<>();
listLong.add(1500L);
listLong.add(8000L);
for (Long val : listLong)
{
System.out.println((val/ divider));
}
Gives me
1.5
8.0
and I want
1.5
8
distance and distance2 are Long and can't be changed.
divider has to be the same thing for both cases.
ANWSER:
Thanks to KevinO
DecimalFormat df = new DecimalFormat("#.##");
for (Long val : listLong)
{
System.out.println(df.format(val/ divider));
}
Try to use DecimalFormat like this:
DecimalFormat df = new DecimalFormat("###.#");
System.out.println(df.format(distance2 / divider));
You can use DecimalFormat to accomplish what you need. Going off of your updated question:
double divider = 1000;
List<Long> listLong = new ArrayList<>();
listLong.add(1500L);
listLong.add(8000L);
DecimalFormat df = new DecimalFormat("###.#");
for (Long val : listLong) {
System.out.println(df.format(val / divider));
}
Produces:
run:
1.5
8
BUILD SUCCESSFUL (total time: 0 seconds)
This is because one of the two parameters are of type double.
You can simply achieve what you are trying to achieve by adding a cast to integer by doing: System.out.println((int) (distance2 / divider));
Also, you should use primitives as it requires only a fraction of the memory. Replace Long with long.
Here is the corrected code snippet:
public static void main (String[] args) throws Exception
{
List<Long> listLong = new ArrayList<>();
listLong.add(1500L);
listLong.add(8000L);
double divider = 1000;
for (long val : listLong)
{
int calc = (int)(val/divider);
if((val/divider - calc) == 0) {
System.out.println(calc);
} else {
System.out.println(val/divider);
}
}
}
Output:
1.5
8
use DecimalFormat
what works for me:
public class NewClass {
public static void main(String[] args) {
DecimalFormat dcf= new DecimalFormat("#.#");
Long distance = 1500L;
Long distance2 = 8000L;
double divider = 1000;
System.out.println(dcf.format(distance / divider));
System.out.println(dcf.format(distance2 / divider));
}
}
output
1.5
8
i'm using a NumberTextWatcher for realtime-edit for an EditText and it's working fine for me,
but some users reporting that they got NumberException problem, i think this is because numbers changes in 123,456 in EditText and cant parse to doubles, so i did some change and use Replace("," , "") code but this is not working for some too!
what should i do so this code works for all of my users?
Thanks and Sorry for my Poor English :D
NumberTextWatcher:
public NumberTextWatcher(EditText et)
{
df = new DecimalFormat("#,###");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("#,###");
this.et = et;
hasFractionalPart = false;
}
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
#Override
public void afterTextChanged(Editable s)
{
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String v = s.toString().replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
Number n = df.parse(v);
int cp = et.getSelectionStart();
if (hasFractionalPart) {
et.setText(df.format(n));
} else {
et.setText(dfnd.format(n));
}
endlen = et.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= et.getText().length()) {
et.setSelection(sel);
} else {
// place cursor at the end?
et.setSelection(et.getText().length() - 1);
}
} catch (NumberFormatException nfe) {
// do nothing?
} catch (ParseException e) {
// do nothing?
}
et.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
{
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
And Here is MainActivity:
final EditText amount = (EditText) findViewById(R.id.editText1);
final EditText duration = (EditText) findViewById(R.id.editText2);
final EditText interest = (EditText) findViewById(R.id.editText3);
amount.addTextChangedListener(new NumberTextWatcher(amount));
duration.addTextChangedListener(new NumberTextWatcher(duration));
interest.addTextChangedListener(new NumberTextWatcher(interest));
String amount1 = amount.getText().toString().replaceAll(",", "");
String duration1 = duration.getText().toString().replaceAll(",", "");
String interest1 = interest.getText().toString().replaceAll(",", "");
try{
double i = Double.parseDouble(amount1);
double j = Double.parseDouble(duration1);
double z = Double.parseDouble(interest1);
}
catch(NumberFormatException e){
}
Programs store and operate on numbers in a locale-independent way. Before displaying or printing a number, a program must convert it to a String that is in a locale-sensitive format. For example, in France the number 123456.78 should be formatted as 123 456,78, and in Germany it should appear as 123.456,78. So you can't just replace "," and ".".
The following code is an example on how to convert from double and back using the current currency of a user in the United States.
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Currency;
import java.util.Locale;
public class Test
{
public static void main(String[] args)
{
Locale currentLocale = Locale.getDefault();
Currency currentCurrency = Currency.getInstance(currentLocale);
Double currencyAmount = new Double(9876543.21);
NumberFormat currencyFormatter =
NumberFormat.getCurrencyInstance(currentLocale);
System.out.println(NumberFormat.getNumberInstance(currentLocale));
System.out.println(
currentLocale.getDisplayName() + ", " +
currentCurrency.getDisplayName() + ": " +
currencyFormatter.format(currencyAmount));
try
{
System.out.println(currencyFormatter.parseObject("$9,876,543.21"));
}
catch (ParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
What else could you do:
* make sure users are entering properly formatted numbers. For example, you should not see any other chars except numbers , . + - $, etc.
* IF you know the user will ALWAYS enter two decimal digits, for example, 10 dollars would be expressed as 10.00 (or 10,00), then you can safely remove all "," and "." and get the original number by dividing it by 100, but remember to also remove spaces (remember France :) )
Also, check the documentation: http://docs.oracle.com/javase/tutorial/i18n/format/numberFormat.html
The problem is probably that other users might be inputting ill-formatted numbers. So you are just replacing the commas with white spaces. Maybe you should replace all non-digit characters?
amount1 = amount.getText().toString().replaceAll("[^\\d]", "");
Or if you want them to leave the decimal points then use the following:
amount1 = amount.getText().toString().replaceAll("[^\\d.]", "");
A 64-bit double can represent integer +/- 253 exactly.
Given this fact, I choose to use a double type as a single type for all my types, since my largest integer is an unsigned 32-bit number.
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?
I have tried String.format("%f", value), which is close, except I get a lot of trailing zeros for small values.
Here's an example output of of %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 other format code?
The answers by Tom E. and Jeremy S. are unacceptable as they both arbitrarily rounds to two decimal places. Please understand the problem before answering.
Please note that String.format(format, args...) is locale-dependent (see answers below).
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.
String.format("%.2f", value);
In short:
If you want to get rid of trailing zeros and locale problems, then you should use:
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
Explanation:
Why other answers did not suit me:
Double.toString() or System.out.println or FloatingDecimal.toJavaFormatString uses scientific notations if double is less than 10^-3 or greater than or equal to 10^7
double myValue = 0.00000021d;
String.format("%s", myvalue); //output: 2.1E-7
by using %f, the default decimal precision is 6, otherwise you can hardcode it, but it results in extra zeros added if you have fewer decimals. Example:
double myValue = 0.00000021d;
String.format("%.12f", myvalue); // Output: 0.000000210000
by using setMaximumFractionDigits(0); or %.0f you remove any decimal precision, which is fine for integers/longs but not for double
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
by using DecimalFormat, you are local dependent. In the French locale, the decimal separator is a comma, not a point:
double myValue = 0.00000021d;
DecimalFormat df = new DecimalFormat("0");
df.setMaximumFractionDigits(340);
System.out.println(df.format(myvalue)); // Output: 0,00000021
Using the ENGLISH locale makes sure you get a point for decimal separator, wherever your program will run.
Why using 340 then for setMaximumFractionDigits?
Two reasons:
setMaximumFractionDigits accepts an integer, but its implementation has a maximum digits allowed of DecimalFormat.DOUBLE_FRACTION_DIGITS which equals 340
Double.MIN_VALUE = 4.9E-324 so with 340 digits you are sure not to round your double and lose precision
Use:
if (d % 1.0 != 0)
return String.format("%s", d);
else
return String.format("%.0f", d);
This should work with the extreme values supported by Double. It yields:
0.12
12
12.144252
0
On my machine, the following function is roughly 7 times faster than the function provided by JasonD's answer, since it avoids String.format:
public static String prettyPrint(double d) {
int i = (int) d;
return d == i ? String.valueOf(i) : String.valueOf(d);
}
My two cents:
if(n % 1 == 0) {
return String.format(Locale.US, "%.0f", n));
} else {
return String.format(Locale.US, "%.1f", n));
}
if (d == Math.floor(d)) {
return String.format("%.0f", d); //Format is: 0 places after decimal point
} else {
return Double.toString(d);
}
More info: https://docs.oracle.com/javase/tutorial/java/data/numberformat.html
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // Choose your Rounding Mode
System.out.println(format.format(price));
This is the result of some tests:
4.30 => 4.3
4.39 => 4.39 // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4 => 4
Naw, never mind. The performance loss due to string manipulation is zero.
And here's the code to trim the end after %f:
private static String trimTrailingZeros(String number) {
if(!number.contains(".")) {
return number;
}
return number.replaceAll("\\.?0*$", "");
}
Use a DecimalFormat and setMinimumFractionDigits(0).
This one will gets the job done nicely:
public static String removeZero(double number) {
DecimalFormat format = new DecimalFormat("#.###########");
return format.format(number);
}
new DecimalFormat("00.#").format(20.236)
//out =20.2
new DecimalFormat("00.#").format(2.236)
//out =02.2
0 for minimum number of digits
Renders # digits
Please note that String.format(format, args...) is locale-dependent because it formats using the user's default locale, that is, probably with commas and even spaces inside like 123 456,789 or 123,456.789, which may be not exactly what you expect.
You may prefer to use String.format((Locale)null, format, args...).
For example,
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));
prints
123456,789000
123456,789000
123456.789000
and this is what will String.format(format, args...) do in different countries.
EDIT Ok, since there has been a discussion about formalities:
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;
}
I made a DoubleFormatter to efficiently convert a great numbers of double values to a nice/presentable string:
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 scientific format (1.2345E+30). Otherwise, display in normal format (124.45678).
the MaxDecimal decide numbers of decimal digits (trim with bankers' rounding)
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) {
// If you do not use the Guava library, replace it with createSharp(precision);
final String sharpByPrecision = Strings.repeat("#", maxFraction_);
NumberFormat f = NumberFormat.getInstance(Locale.US);
// Apply bankers' 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 outside 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) {
// If you do not use Guava library, replace with createSharp(precision);
final String sharpByPrecision = Strings.repeat("#", maxFraction_);
final double absv = Math.abs(v);
NumberFormat f = NumberFormat.getInstance(Locale.US);
// Apply bankers' 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 two functions from the Guava library. If you don't use Guava, code it yourself:
/**
* 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 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);
}
}
You said you choose to store your numbers with the double type. I think this could be the root of the problem, because it forces you to store integers into doubles (and therefore losing the initial information about the value's nature). What about storing your numbers in instances of the Number class (superclass of both Double and Integer) and rely on polymorphism to determine the correct format of each number?
I know it may not be acceptable to refactor a whole part of your code due to that, but it could produce the desired output without extra code/casting/parsing.
Example:
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);
}
}
}
Will produce the following output:
232
0.18
1237875192
4.58
0
1.2345
For Kotlin you can use an extension like:
fun Double.toPrettyString() =
if(this - this.toLong() == 0.0)
String.format("%d", this.toLong())
else
String.format("%s", this)
This is what I came up with:
private static String format(final double dbl) {
return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
}
It is a simple one-liner and only casts to int if it really needs to.
Format price with grouping, rounding, and no unnecessary zeroes (in double).
Rules:
No zeroes at the end (2.0000 = 2; 1.0100000 = 1.01)
Two digits maximum after a point (2.010 = 2.01; 0.20 = 0.2)
Rounding after the 2nd digit after a point (1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
Returns 0 (null/-0 = 0)
Adds $ (= $56/-$56)
Grouping (101101.02 = $101,101.02)
More examples:
-99.985 = -$99.99
10 = $10
10.00 = $10
20.01000089 = $20.01
It is written in Kotlin as a fun extension of Double (because it is used in Android), but it can be converted to Java easily, because Java classes were used.
/**
* 23.0 -> $23
*
* 23.1 -> $23.1
*
* 23.01 -> $23.01
*
* 23.99 -> $23.99
*
* 23.999 -> $24
*
* -0.0 -> $0
*
* -5.00 -> -$5
*
* -5.019 -> -$5.02
*/
fun Double?.formatUserAsSum(): String {
return when {
this == null || this == 0.0 -> "$0"
this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
}
}
How to use:
var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20
yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0
About DecimalFormat: https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html
A simple solution with locale in mind:
double d = 123.45;
NumberFormat numberFormat = NumberFormat.getInstance(Locale.GERMANY);
System.out.println(numberFormat.format(d));
Since comma is used as decimal separator in Germany, the above will print:
123,45
Here's another answer that has an option to append decimal ONLY IF decimal was not zero.
/**
* Example: (isDecimalRequired = true)
* d = 12345
* returns 12,345.00
*
* d = 12345.12345
* returns 12,345.12
*
* ==================================================
* Example: (isDecimalRequired = false)
* d = 12345
* returns 12,345 (notice that there's no decimal since it's zero)
*
* d = 12345.12345
* returns 12,345.12
*
* #param d float to format
* #param zeroCount number decimal places
* #param isDecimalRequired true if it will put decimal even zero,
* false will remove the last decimal(s) if zero.
*/
fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
val zeros = StringBuilder()
for (i in 0 until zeroCount) {
zeros.append("0")
}
var pattern = "#,##0"
if (zeros.isNotEmpty()) {
pattern += ".$zeros"
}
val numberFormat = DecimalFormat(pattern)
var formattedNumber = if (d != null) numberFormat.format(d) else "0"
if (!isDecimalRequired) {
for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
val number = formattedNumber[i - 1]
if (number == '0' || number == '.') {
formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
} else {
break
}
}
}
return formattedNumber
}
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;
}
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);
}
I had to use this because d == (long)d was giving me violation in a SonarQube report.
I am using this for formatting numbers without trailing zeroes in our JSF application. The original built-in formatters required you to specify max numbers of fractional digits which could be useful here also in case you have too many fractional digits.
/**
* Formats the given Number as with as many fractional digits as precision
* available.<br>
* This is a convenient method in case all fractional digits shall be
* rendered and no custom format / pattern needs to be provided.<br>
* <br>
* This serves as a workaround for {#link NumberFormat#getNumberInstance()}
* which by default only renders up to three fractional digits.
*
* #param number
* #param locale
* #param groupingUsed <code>true</code> if grouping shall be used
*
* #return
*/
public static String formatNumberFraction(final Number number, final Locale locale, final boolean groupingUsed)
{
if (number == null)
return null;
final BigDecimal bDNumber = MathUtils.getBigDecimal(number);
final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
numberFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale()));
numberFormat.setGroupingUsed(groupingUsed);
// Convert back for locale percent formatter
return numberFormat.format(bDNumber);
}
/**
* Formats the given Number as percent with as many fractional digits as
* precision available.<br>
* This is a convenient method in case all fractional digits shall be
* rendered and no custom format / pattern needs to be provided.<br>
* <br>
* This serves as a workaround for {#link NumberFormat#getPercentInstance()}
* which does not renders fractional digits.
*
* #param number Number in range of [0-1]
* #param locale
*
* #return
*/
public static String formatPercentFraction(final Number number, final Locale locale)
{
if (number == null)
return null;
final BigDecimal bDNumber = MathUtils.getBigDecimal(number).multiply(new BigDecimal(100));
final NumberFormat percentScaleFormat = NumberFormat.getPercentInstance(locale);
percentScaleFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale() - 2));
final BigDecimal bDNumberPercent = bDNumber.multiply(new BigDecimal(0.01));
// Convert back for locale percent formatter
final String strPercent = percentScaleFormat.format(bDNumberPercent);
return strPercent;
}
work with given decimal length...
public static String getLocaleFloatValueDecimalWithLength(Locale loc, float value, int length) {
//make string from float value
return String.format(loc, (value % 1 == 0 ? "%.0f" : "%."+length+"f"), value);
}
0.0 -> 0%
1.0 -> 100%
0.1 -> 10%
0.11 -> 11%
0.01 -> 1%
0.111 -> 11.1%
0.001 -> 0.1%
0.1111 -> 11.11%
0.0001 -> 0.01%
".replace()" is added because I was always getting wrong separator
import java.text.NumberFormat
fun Double.formating(): String {
val defaultFormat: NumberFormat = NumberFormat.getPercentInstance()
defaultFormat.minimumFractionDigits = 0
defaultFormat.maximumFractionDigits = 2
return defaultFormat.format(this).replace(",", ".")
}
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*$", "");
}
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 two decimal digits. So will DecimalFormat. If you need all the decimal places, but not the trailing zeros then this works best.
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.
A 64-bit double can represent integer +/- 253 exactly.
Given this fact, I choose to use a double type as a single type for all my types, since my largest integer is an unsigned 32-bit number.
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?
I have tried String.format("%f", value), which is close, except I get a lot of trailing zeros for small values.
Here's an example output of of %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 other format code?
The answers by Tom E. and Jeremy S. are unacceptable as they both arbitrarily rounds to two decimal places. Please understand the problem before answering.
Please note that String.format(format, args...) is locale-dependent (see answers below).
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.
String.format("%.2f", value);
In short:
If you want to get rid of trailing zeros and locale problems, then you should use:
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
Explanation:
Why other answers did not suit me:
Double.toString() or System.out.println or FloatingDecimal.toJavaFormatString uses scientific notations if double is less than 10^-3 or greater than or equal to 10^7
double myValue = 0.00000021d;
String.format("%s", myvalue); //output: 2.1E-7
by using %f, the default decimal precision is 6, otherwise you can hardcode it, but it results in extra zeros added if you have fewer decimals. Example:
double myValue = 0.00000021d;
String.format("%.12f", myvalue); // Output: 0.000000210000
by using setMaximumFractionDigits(0); or %.0f you remove any decimal precision, which is fine for integers/longs but not for double
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
by using DecimalFormat, you are local dependent. In the French locale, the decimal separator is a comma, not a point:
double myValue = 0.00000021d;
DecimalFormat df = new DecimalFormat("0");
df.setMaximumFractionDigits(340);
System.out.println(df.format(myvalue)); // Output: 0,00000021
Using the ENGLISH locale makes sure you get a point for decimal separator, wherever your program will run.
Why using 340 then for setMaximumFractionDigits?
Two reasons:
setMaximumFractionDigits accepts an integer, but its implementation has a maximum digits allowed of DecimalFormat.DOUBLE_FRACTION_DIGITS which equals 340
Double.MIN_VALUE = 4.9E-324 so with 340 digits you are sure not to round your double and lose precision
Use:
if (d % 1.0 != 0)
return String.format("%s", d);
else
return String.format("%.0f", d);
This should work with the extreme values supported by Double. It yields:
0.12
12
12.144252
0
On my machine, the following function is roughly 7 times faster than the function provided by JasonD's answer, since it avoids String.format:
public static String prettyPrint(double d) {
int i = (int) d;
return d == i ? String.valueOf(i) : String.valueOf(d);
}
My two cents:
if(n % 1 == 0) {
return String.format(Locale.US, "%.0f", n));
} else {
return String.format(Locale.US, "%.1f", n));
}
if (d == Math.floor(d)) {
return String.format("%.0f", d); //Format is: 0 places after decimal point
} else {
return Double.toString(d);
}
More info: https://docs.oracle.com/javase/tutorial/java/data/numberformat.html
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // Choose your Rounding Mode
System.out.println(format.format(price));
This is the result of some tests:
4.30 => 4.3
4.39 => 4.39 // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4 => 4
Naw, never mind. The performance loss due to string manipulation is zero.
And here's the code to trim the end after %f:
private static String trimTrailingZeros(String number) {
if(!number.contains(".")) {
return number;
}
return number.replaceAll("\\.?0*$", "");
}
Use a DecimalFormat and setMinimumFractionDigits(0).
This one will gets the job done nicely:
public static String removeZero(double number) {
DecimalFormat format = new DecimalFormat("#.###########");
return format.format(number);
}
new DecimalFormat("00.#").format(20.236)
//out =20.2
new DecimalFormat("00.#").format(2.236)
//out =02.2
0 for minimum number of digits
Renders # digits
Please note that String.format(format, args...) is locale-dependent because it formats using the user's default locale, that is, probably with commas and even spaces inside like 123 456,789 or 123,456.789, which may be not exactly what you expect.
You may prefer to use String.format((Locale)null, format, args...).
For example,
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));
prints
123456,789000
123456,789000
123456.789000
and this is what will String.format(format, args...) do in different countries.
EDIT Ok, since there has been a discussion about formalities:
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;
}
I made a DoubleFormatter to efficiently convert a great numbers of double values to a nice/presentable string:
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 scientific format (1.2345E+30). Otherwise, display in normal format (124.45678).
the MaxDecimal decide numbers of decimal digits (trim with bankers' rounding)
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) {
// If you do not use the Guava library, replace it with createSharp(precision);
final String sharpByPrecision = Strings.repeat("#", maxFraction_);
NumberFormat f = NumberFormat.getInstance(Locale.US);
// Apply bankers' 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 outside 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) {
// If you do not use Guava library, replace with createSharp(precision);
final String sharpByPrecision = Strings.repeat("#", maxFraction_);
final double absv = Math.abs(v);
NumberFormat f = NumberFormat.getInstance(Locale.US);
// Apply bankers' 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 two functions from the Guava library. If you don't use Guava, code it yourself:
/**
* 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 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);
}
}
You said you choose to store your numbers with the double type. I think this could be the root of the problem, because it forces you to store integers into doubles (and therefore losing the initial information about the value's nature). What about storing your numbers in instances of the Number class (superclass of both Double and Integer) and rely on polymorphism to determine the correct format of each number?
I know it may not be acceptable to refactor a whole part of your code due to that, but it could produce the desired output without extra code/casting/parsing.
Example:
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);
}
}
}
Will produce the following output:
232
0.18
1237875192
4.58
0
1.2345
For Kotlin you can use an extension like:
fun Double.toPrettyString() =
if(this - this.toLong() == 0.0)
String.format("%d", this.toLong())
else
String.format("%s", this)
This is what I came up with:
private static String format(final double dbl) {
return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
}
It is a simple one-liner and only casts to int if it really needs to.
Format price with grouping, rounding, and no unnecessary zeroes (in double).
Rules:
No zeroes at the end (2.0000 = 2; 1.0100000 = 1.01)
Two digits maximum after a point (2.010 = 2.01; 0.20 = 0.2)
Rounding after the 2nd digit after a point (1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
Returns 0 (null/-0 = 0)
Adds $ (= $56/-$56)
Grouping (101101.02 = $101,101.02)
More examples:
-99.985 = -$99.99
10 = $10
10.00 = $10
20.01000089 = $20.01
It is written in Kotlin as a fun extension of Double (because it is used in Android), but it can be converted to Java easily, because Java classes were used.
/**
* 23.0 -> $23
*
* 23.1 -> $23.1
*
* 23.01 -> $23.01
*
* 23.99 -> $23.99
*
* 23.999 -> $24
*
* -0.0 -> $0
*
* -5.00 -> -$5
*
* -5.019 -> -$5.02
*/
fun Double?.formatUserAsSum(): String {
return when {
this == null || this == 0.0 -> "$0"
this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
}
}
How to use:
var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20
yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0
About DecimalFormat: https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html
A simple solution with locale in mind:
double d = 123.45;
NumberFormat numberFormat = NumberFormat.getInstance(Locale.GERMANY);
System.out.println(numberFormat.format(d));
Since comma is used as decimal separator in Germany, the above will print:
123,45
Here's another answer that has an option to append decimal ONLY IF decimal was not zero.
/**
* Example: (isDecimalRequired = true)
* d = 12345
* returns 12,345.00
*
* d = 12345.12345
* returns 12,345.12
*
* ==================================================
* Example: (isDecimalRequired = false)
* d = 12345
* returns 12,345 (notice that there's no decimal since it's zero)
*
* d = 12345.12345
* returns 12,345.12
*
* #param d float to format
* #param zeroCount number decimal places
* #param isDecimalRequired true if it will put decimal even zero,
* false will remove the last decimal(s) if zero.
*/
fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
val zeros = StringBuilder()
for (i in 0 until zeroCount) {
zeros.append("0")
}
var pattern = "#,##0"
if (zeros.isNotEmpty()) {
pattern += ".$zeros"
}
val numberFormat = DecimalFormat(pattern)
var formattedNumber = if (d != null) numberFormat.format(d) else "0"
if (!isDecimalRequired) {
for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
val number = formattedNumber[i - 1]
if (number == '0' || number == '.') {
formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
} else {
break
}
}
}
return formattedNumber
}
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;
}
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);
}
I had to use this because d == (long)d was giving me violation in a SonarQube report.
I am using this for formatting numbers without trailing zeroes in our JSF application. The original built-in formatters required you to specify max numbers of fractional digits which could be useful here also in case you have too many fractional digits.
/**
* Formats the given Number as with as many fractional digits as precision
* available.<br>
* This is a convenient method in case all fractional digits shall be
* rendered and no custom format / pattern needs to be provided.<br>
* <br>
* This serves as a workaround for {#link NumberFormat#getNumberInstance()}
* which by default only renders up to three fractional digits.
*
* #param number
* #param locale
* #param groupingUsed <code>true</code> if grouping shall be used
*
* #return
*/
public static String formatNumberFraction(final Number number, final Locale locale, final boolean groupingUsed)
{
if (number == null)
return null;
final BigDecimal bDNumber = MathUtils.getBigDecimal(number);
final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
numberFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale()));
numberFormat.setGroupingUsed(groupingUsed);
// Convert back for locale percent formatter
return numberFormat.format(bDNumber);
}
/**
* Formats the given Number as percent with as many fractional digits as
* precision available.<br>
* This is a convenient method in case all fractional digits shall be
* rendered and no custom format / pattern needs to be provided.<br>
* <br>
* This serves as a workaround for {#link NumberFormat#getPercentInstance()}
* which does not renders fractional digits.
*
* #param number Number in range of [0-1]
* #param locale
*
* #return
*/
public static String formatPercentFraction(final Number number, final Locale locale)
{
if (number == null)
return null;
final BigDecimal bDNumber = MathUtils.getBigDecimal(number).multiply(new BigDecimal(100));
final NumberFormat percentScaleFormat = NumberFormat.getPercentInstance(locale);
percentScaleFormat.setMaximumFractionDigits(Math.max(0, bDNumber.scale() - 2));
final BigDecimal bDNumberPercent = bDNumber.multiply(new BigDecimal(0.01));
// Convert back for locale percent formatter
final String strPercent = percentScaleFormat.format(bDNumberPercent);
return strPercent;
}
work with given decimal length...
public static String getLocaleFloatValueDecimalWithLength(Locale loc, float value, int length) {
//make string from float value
return String.format(loc, (value % 1 == 0 ? "%.0f" : "%."+length+"f"), value);
}
0.0 -> 0%
1.0 -> 100%
0.1 -> 10%
0.11 -> 11%
0.01 -> 1%
0.111 -> 11.1%
0.001 -> 0.1%
0.1111 -> 11.11%
0.0001 -> 0.01%
".replace()" is added because I was always getting wrong separator
import java.text.NumberFormat
fun Double.formating(): String {
val defaultFormat: NumberFormat = NumberFormat.getPercentInstance()
defaultFormat.minimumFractionDigits = 0
defaultFormat.maximumFractionDigits = 2
return defaultFormat.format(this).replace(",", ".")
}
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*$", "");
}
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 two decimal digits. So will DecimalFormat. If you need all the decimal places, but not the trailing zeros then this works best.
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.