Converting different countrys currency to double using java [duplicate]

Solution 1:

Never use double for representing exact amounts

Well, of course you can do, but you need to really understand floating point arithmetic

Use a NumberFormat. Whilst it does handle some currencies, it's usually easier just to strip all the currency symbols. The NumberFormat will use Locale to work out the delimiters to use:

public static BigDecimal parse(final String amount, final Locale locale) throws ParseException {
    final NumberFormat format = NumberFormat.getNumberInstance(locale);
    if (format instanceof DecimalFormat) {
        ((DecimalFormat) format).setParseBigDecimal(true);
    }
    return (BigDecimal) format.parse(amount.replaceAll("[^\\d.,]",""));
}

This takes a String of the amount and the Locale. It then creates a BigDecimal parsing NumberFormat instance. It uses replaceAll and regex to strip all but digits, . and , from the number then parses it.

A quick demo against your examples:

public static void main(String[] args) throws ParseException {
    final String dollarsA = "$199.00";
    final String real = "R$ 399,00";
    final String dollarsB = "£25.00";
    final String tailingEuro = "90,83 €";
    final String dollarsC = "$199.00";
    final String dirham = "AED 449.00";

    System.out.println(parse(dollarsA, Locale.US));
    System.out.println(parse(real, Locale.FRANCE));
    System.out.println(parse(dollarsB, Locale.US));
    System.out.println(parse(tailingEuro, Locale.FRANCE));
    System.out.println(parse(dollarsC, Locale.US));
    System.out.println(parse(dirham, Locale.US));
}

Output:

199.00
399.00
25.00
90.83
199.00
449.00

I have simply used US where the decimal is . and FRANCE where the decimal is , but you could use the correct Locale for the currency if you wish.

Solution 2:

As David Wallace said, you don't (or if you do, you do it very carefully and only if deeply informed about the issues), because double is usually not appropriate for use with currency values. It has issues like 0.1 + 0.2 ending up being 0.30000000000000004 (and other such). BigDecimal used properly is probably better suited, although dramatically slower. (More on "used properly" below.)

First, you have to determine what the thousands and decimal separators being used are, from the locale (see Boris's excellent answer for more on that) or by having that information provided along with the string. In some cultures, they're , (thousands) and . (decimal), e.g. "1,000.24", but in other cultures it's the other way around (e.g., "1.000,24"). For all I know, there are other characters used in some places. (You can't guess this from the string, you have no idea whether "1,234" means one thousand two hundred and thirty-four or one point two three four.)

Once you know what they are, you want to remove the thousands separators, convert the decimal to . (specifically), and then remove all non-digit, non-minus, non-decimals from the string:

// where `currencyString` is the string containing the currency
currencyString = currencyString
                     .replace(thousandsString, "")
                     .replace(decimalString, ".")
                     .replaceAll("[^\\d.-]", "");

Then construct your BigDecimal:

BigDecimal currency = new BigDecimal(currencyString);

...and set the scale and rounding mode appropriate to your program. Also be sure to read the Javadoc carefully for the various operation methods. For instance, as Peter Lawrey pointed out in the comments, a naive use of BigDecimal will give you runtime exceptions:

BigDecimal bd = new BigDecimal(43);
bd = bd.divide(new BigDecimal(7)); // Throws ArithmeticException

You can deal with those by providing rounding information:

BigDecimal bd = new BigDecimal(43);
bd = bd.divide(new BigDecimal(7), RoundingMode.HALF_UP);

...but again, be sure to set a scale appropriate to what you're doing. For instance, if your scale is only 2, then (43 / 7) * 7 with RoundingMode.HALF_UP will be 42.98 instead of 43. Dividing is particularly tricky, you may need significant scale and then a final rounding back to the more normal currency-level scales (typically 2, but sometimes you may want more places to the right than that).


If you really, really, really want a double, use Double.parseDouble:

// where `currencyString` is the string containing the currency
currencyString = currencyString
                     .replace(thousandsString, "")
                     .replace(decimalString, ".")
                     .replaceAll("[^\\d.-]", "");
double thisWillHaveIssues = Double.parseDouble(currencyString);