13

I have once scenario where in i get the currencies as a String, for eg:

$199.00

R$ 399,00

£25.00

90,83 €

AED 449.00

How do i convert these currencies to double in java?

user2649233
  • 912
  • 4
  • 14
  • 28
  • 9
    You don't. You should NEVER use a `double` for storing an amount of money. – Dawood ibn Kareem Jun 02 '14 at 09:02
  • 1
    You will do much better using joda money library. http://www.joda.org/joda-money/ – Mirek Surma Jun 02 '14 at 09:04
  • 1
    @DavidWallace It is worth remembering most trading systems and investment banks use `double`, many in C++, but you have to use rounding as well. – Peter Lawrey Jun 02 '14 at 09:12
  • 1
    @DavidWallace I recently spend up a risk system by over 20x by removing 10% of the use of BigDecimal and replacing it with double (and getting the same answers ;) – Peter Lawrey Jun 02 '14 at 09:13
  • 1
    If you got all the same answers, then you didn't ask enough questions. In the last few years, I have worked for both a major bank, and a funds manager. Both multi-nationals. In both organisations, using a `double` would have been almost a sackable offense. So I strongly doubt your assertion that this is the data type used by "most trading systems and investment banks". – Dawood ibn Kareem Jun 02 '14 at 09:19
  • @DavidWallace for keeping accounts, sure. For risk management, simulations, etc. performance is more important. The error range of double calculations can be managed, you just need to be aware of it. – Erwin Bolwidt Jun 02 '14 at 09:52
  • @ErwinBolwidt I agree completely. – Dawood ibn Kareem Jun 02 '14 at 09:58

4 Answers4

28

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.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
3

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);
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • You probably should preserve the decimal point. BTW Most investment banks use `double`, but you are right that `double` is not for novices. You need to know how to use rounding. Most developers have enough to worry about would needing to know how to use `double` correctly. BTW try this in BigDecimal and see how you go `(43 / 7) * 7` (From a real financial system ;) – Peter Lawrey Jun 02 '14 at 09:11
  • @PeterLawrey: I had the `replace` and `replaceAll` backward, and was failing to remove thousands separators first. :-) I've fixed those... – T.J. Crowder Jun 02 '14 at 09:13
  • @DhrubajyotiGogoi: You may be looking at an outdated copy of the answer (see my comment above). – T.J. Crowder Jun 02 '14 at 09:14
  • This works provided you don't have a number like `5.555,50` It proves the point that there is much bigger things to worry about the an little representation error. – Peter Lawrey Jun 02 '14 at 09:15
  • What about `NumberFormat.getCurrencyInstance`? The `Locale` will deal with the separators. It will even deal with (some) currency symbols. – Boris the Spider Jun 02 '14 at 09:17
  • @PeterLawrey: Works just fine with that input: http://ideone.com/LSeMUp Perhaps you didn't see the update with my comment above? – T.J. Crowder Jun 02 '14 at 09:20
  • @BoristheSpider: Sounds like that would make a good answer, better than this one as I have that hand-waving around the separators. – T.J. Crowder Jun 02 '14 at 09:21
  • Bearing in mind that one Bahraini dinar consists of 1000 fuloos, how can you tell whether the comma in `1,250 BHD` is a decimal point or a thousands separator? – Dawood ibn Kareem Jun 02 '14 at 09:22
  • @DavidWallace: I wasn't saying you would guess it from the string. As you say, you can't. I was saying, determine what they are (from the locale, by having the user giving you the value tell you, etc.). (I've updated to make that clearer.) – T.J. Crowder Jun 02 '14 at 09:23
  • 1
    Right, sorry, I don't know why I thought you had claimed this. But thank you for adding the latest sentence to your answer. – Dawood ibn Kareem Jun 02 '14 at 09:25
  • @DavidWallace: Not just you, re-reading it was definitely not clear enough at first. :-) – T.J. Crowder Jun 02 '14 at 09:26
  • @PeterLawrey: Re `(43 / 7) * 7`, I called out using scale and rounding modes in the original answer, but I've expanded that to make the point more firmly. – T.J. Crowder Jun 02 '14 at 09:38
  • @T.J.Crowder +1 well clarified. Of course you can do rounding with double as well and get the same result. – Peter Lawrey Jun 02 '14 at 09:40
  • @PeterLawrey: Indeed, you just get the precision inherent with `double` (and much faster results), rather than being able to choose your precision. (And don't get me wrong, if you've worked on financial systems, I'm absolutely certain you know a **lot** more about the issues with `double` and `BigDecimal` and similar than I do.) – T.J. Crowder Jun 02 '14 at 09:43
  • @T.J.Crowder It does annoy me when developers say `Never use double for money` when you end up with a cluster of six servers doing the job of less than half of one. (this is a real case I am dealing with at the moment) Using C# `decimal`, as the case may be, has effectively preventing the software from expanding/being extended for many years because they couldn't justify more machines, which wouldn't have been required in the first place. – Peter Lawrey Jun 02 '14 at 09:51
  • 1
    @PeterLawrey on a related note - do you have a good reference for techniques and ticks when using doubles and floats with money or other calculations that require accuracy to a number of decimal places? – Boris the Spider Jun 03 '14 at 15:23
  • 1
    @BoristheSpider There is not a lot to say. This might be a good start. http://vanillajava.blogspot.com/2011/08/double-your-money-again.html – Peter Lawrey Jun 03 '14 at 22:18
0

Use contains,replace and valueOf

if(line.constains("$")){
     double value=Double.valueOf(line.replace("$",""));
}
if(line.constains("£")){
     double value=Double.valueOf(line.replace("£",""));
}
//same with others
SteveL
  • 3,331
  • 4
  • 32
  • 57
  • 1
    Why not just use a regular expression that matches anything that isn't a digit, a comma, a minus or a full stop? Otherwise, you have to do an awful lot of separate replacements. – Dawood ibn Kareem Jun 02 '14 at 09:27
0

A simpler way is to use DecimalFormal as in the following example:

String myMoney = 90,83 €

String s

try {
s = DecimalFormat.getCurrencyInstance(Locale.getDefault()).parse (myMoney).toString()
} catch (ParseException e) {
return;
}

double d  = Double.parseDouble(s);