I have some code that is used to convert a decimal currency to an integer with precision being a function of which currency is being converted.
This code is more of a "brute-force" method to do the conversion and just doesn't feel right to me. Is there a better way to achieve the result?
public static int CurrencyToInt(decimal amount, string currencyCode)
{
var precision = 0;
currencyCode = currencyCode?.ToUpperInvariant();
if (new[] {
"BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"
}.Contains(currencyCode))
{
precision = 3;
}
else if (new[] {
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BMD",
"BND", "BOB", "BRL", "BSD", "BTN", "BWP", "BZD", "CAD", "CDF", "CHF", "CNY", "COP", "CRC", "CUC", "CUP",
"CZK", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP",
"GMD", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IRR", "JEP", "JMD",
"KES", "KGS", "KHR", "KPW", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LVL", "MAD", "MDL",
"MKD", "MMK", "MNT", "MOP", "MUR", "MVP", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK",
"NPR", "NZD", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "QAR", "RON", "RSD", "RUB", "SAR", "SBD", "SCR",
"SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SPL", "SRD", "STD", "SVC", "SYP", "SZL", "THB", "TJS", "TMT",
"TOP", "TRY", "TTD", "TVD", "TWD", "TZS", "UAH", "USD", "UYU", "UZS", "VEF", "WST", "XCD", "XDR", "YER",
"ZAR", "ZMW", "ZWD"
}.Contains(currencyCode))
{
precision = 2;
}
else if (new[] {
"MGA", "MRO"
}.Contains(currencyCode))
{
precision = 1;
}
else if (new[]{
"BIF", "BYR", "CLP", "CVE", "DJF", "GNF", "ISK", "JPY", "KMF", "KRW", "PYG", "RWF", "UGX", "VND", "VUV",
"XAF", "XOF", "XPF"
}.Contains(currencyCode))
{
precision = 0;
}
else
{
throw new ArgumentException("Unknown currency code " + currencyCode, nameof(currencyCode));
}
while (precision > 0)
{
amount *= 10;
precision--;
}
return (int)Math.Floor(amount);
}
-
1\$\begingroup\$ Why not to create static Dictionary<string, int> instead? \$\endgroup\$pgs– pgs2018年05月24日 16:56:50 +00:00Commented May 24, 2018 at 16:56
-
1\$\begingroup\$ Suggest reading up on CultureInfo Class and CultureInfo.NumberFormat in particular. Let the framework help you. CultureInfo includes country codes and currency symbol. \$\endgroup\$Rick Davin– Rick Davin2018年05月24日 17:19:05 +00:00Commented May 24, 2018 at 17:19
-
1\$\begingroup\$ amount *= Math.Pow(10, precision); \$\endgroup\$paparazzo– paparazzo2018年05月24日 17:27:12 +00:00Commented May 24, 2018 at 17:27
1 Answer 1
Maybe it is intentionally(?):
I would expect CurrencyToInt(234.567m, "USD")
to return 23457
, but it returns 23456
?
Instead of the rather unclear if-statements it is more readable with a switch:
public static int CurrencyToInt(decimal amount, string currencyCode)
{
var precision = 0;
currencyCode = currencyCode?.ToUpperInvariant();
switch (currencyCode)
{
case "BHD": case "IQD": case "JOD": case "KWD": case "LYD": case "OMR": case "TND":
precision = 3;
break;
case "AED": case "AFN": case "ALL": case "AMD": case "ANG": case "AOA": case "ARS": case "AUD": case "AWG": case "AZN": case "BAM": case "BBD": case "BDT": case "BGN": case "BMD":
case "BND": case "BOB": case "BRL": case "BSD": case "BTN": case "BWP": case "BZD": case "CAD": case "CDF": case "CHF": case "CNY": case "COP": case "CRC": case "CUC": case "CUP":
case "CZK": case "DKK": case "DOP": case "DZD": case "EGP": case "ERN": case "ETB": case "EUR": case "FJD": case "FKP": case "GBP": case "GEL": case "GGP": case "GHS": case "GIP":
....
As stated in the comments the whole thing can be boiled down to 3 lines of code, if you use the framework:
public static int CurrencyToIntByCulture(decimal amount, string currencyCode)
{
CultureInfo culture = new CultureInfo(currencyCode);
int precision = culture.NumberFormat.CurrencyDecimalDigits;
return (int)(amount * (decimal)Math.Pow(10, precision));
// Or?: return (int)Math.Round((amount * (decimal)Math.Pow(10, precision)));
}
But culture.NumberFormat.CurrencyDecimalDigits
returns different numbers of decimals for certain cultures, so maybe you have special needs?
-
\$\begingroup\$ As to the special needs, it seems better to abstract this in a separate method anyway. Defining the precision and applying it are two different things. It doesn't matter for your example, but if it's a custom mapping, it does start to matter more. That would mean that your 3 line example would turn into a 2 line example, where
int precision = getCustomPrecision(currencyCode);
. \$\endgroup\$Flater– Flater2018年05月25日 11:19:49 +00:00Commented May 25, 2018 at 11:19 -
\$\begingroup\$ @Flater: You may be right, but we know to little about the context and needs. If you have to overrule some of the culture defined precisions then a separate function is appropriate for that. \$\endgroup\$user73941– user739412018年05月25日 12:53:22 +00:00Commented May 25, 2018 at 12:53