0
\$\begingroup\$

I have to show price of an item. If the price is only in dollars, it should look like an integer. But if it contains cents, it should look like double value and always have two symbols after decimal separator:

2 550$
2 550,55$
2 550,50$

I decided to use Decimal format for this task, but I don't know how to properly handle the cents.

Here is my solution:

public static String moneyFormat(double amount, String currency) {
    DecimalFormatSymbols dfSymbols = new DecimalFormatSymbols();
    dfSymbols.setDecimalSeparator(',');
    dfSymbols.setGroupingSeparator(' ');
    dfSymbols.setCurrencySymbol(currency);
    DecimalFormat df;
    if (isInteger(amount)) {
        df = new DecimalFormat("###,###,##0 \u00A4", dfSymbols);
    } else {
        df = new DecimalFormat("###,###,##0.00 \u00A4", dfSymbols);
    }
    df.setGroupingUsed(true);
    return df.format(amount);
}

private static boolean isInteger(double amount) {
    return  !Double.isInfinite(amount) && (amount == Math.floor(amount));
}

The amount comes from server in Double, therefore, probably, we can have some errors in case if 2$ will be represented as 1,999(9)$ but I am not sure how to test for such errors.

As an alternative to passing two patterns I can use df.setMaximumFractionDigits();:

 df = new DecimalFormat("###,###,##0.00 \u00A4", dfSymbols);
if (isInteger(amount)) {
    df.setMaximumFractionDigits(0);
} else {
    df.setMaximumFractionDigits(2);
}

But the question stays the same: can I handle integers in some other, cleaner way?

Update

Unfortunately, changing output from server to String or other types is not an option. And I forgot to mention, that it for Android. I don't use classes of platform, so I haven't added it to tags.

\$\endgroup\$
  • \$\begingroup\$ stackoverflow.com/a/13791422/648075 ? \$\endgroup\$ – BCdotWEB Dec 16 '16 at 12:03
  • 1
    \$\begingroup\$ What do you mean by "it comes from the server as Double"? If you have somehow the option to get it as String prefer this (In combination with BigDecimal). An other approach is Integer of cent. Just get rid of the double as soon as possible at your end. \$\endgroup\$ – mheinzerling Dec 16 '16 at 12:07
  • \$\begingroup\$ @BCdotWEB thank you, I will try it. Will I still be able to set grouping and decimal separators explicitly? \$\endgroup\$ – Gaket Dec 16 '16 at 12:14
  • \$\begingroup\$ @mnhg, unfortunately, it is not an option \$\endgroup\$ – Gaket Dec 16 '16 at 12:19
  • \$\begingroup\$ @BCdotWEB, unfortunately, I haven't found a way not to include unneeded cents or make groupging using NumberFormat.getCurrencyInstance() \$\endgroup\$ – Gaket Dec 16 '16 at 12:37
5
\$\begingroup\$

I know that you receive double, but you shouldn't get your system "infected" by double prices. This is just wrong. Convert them once you received them.

Instead of comparing your Doubles to find the Integer, you could also just work with the String and replace the zeros. This will also work with your double values and is a valid approach if you just want to render the number.

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

public class Main
{
    public static void main(String... args)
    {
        System.out.println(format(new BigDecimal("2.1111")));
        System.out.println(format(new BigDecimal("5454542.1111")));
        System.out.println(format(new BigDecimal("5454542.1")));
        System.out.println(format(new BigDecimal("5454542")));
    }

    private static String format(BigDecimal price)
    {
        return createFormat().format(price).replace(",00", "");
    }

    private static DecimalFormat createFormat()
    {
        DecimalFormatSymbols dfSymbols = new DecimalFormatSymbols();
        dfSymbols.setDecimalSeparator(',');
        dfSymbols.setGroupingSeparator(' ');
        final String currency = "$";
        dfSymbols.setCurrencySymbol(currency);
        return new DecimalFormat("###,###,##0.00 \u00A4", dfSymbols);
    }
}

This will give you:

2,11 $
5 454 542,11 $
5 454 542,10 $
5 454 542 $
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You are hard coding a ',' as the decimal separator. Depending on the requirement, it might be better to use the appropriate decimal separator for the locale. \$\endgroup\$ – rossum Dec 17 '16 at 13:07
  • \$\begingroup\$ @rossum , you are right. I would extract it in a utility class and reuse ',' as a constant or maybe store the format in an intermediate variable to get the used DecimalSeparator. \$\endgroup\$ – mheinzerling Dec 17 '16 at 16:03
1
\$\begingroup\$

You should handle the case of NaN in isInteger if you're using doubles. All these gotchas are the reason why you should use java.math.BigDecimal for financial calculations.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thank you. I forgot to mention that it is for a mobile platform and the only use for these doubles is showing them to client, wtihout any other actions. \$\endgroup\$ – Gaket Dec 16 '16 at 13:01
  • 3
    \$\begingroup\$ @Gaket, Even then, you should always use BigDecimal for anything involved in finances. Even on Android, J2ME, or whatever. \$\endgroup\$ – Tamoghna Chowdhury Dec 16 '16 at 13:03

Not the answer you're looking for? Browse other questions tagged or ask your own question.