String to double currency has wrong result ( comma point issue) - c#

Now i have this problem here with comma and point in decimal point and thousands separator.
My program gets prices from different sources .
some american some european
Some prices comes like this 2000,0.20 for 20000.20
and some like 2000.0,20 for again 20000.20
I couldn't find a way to make my code to recognize these two formats .
i tried to use replace to turn the comma to dot but if there is thousands separator in the number i get problems. how can i convert string to double without these kind of problems ?
I tried this
but its just dont work if there is two different culture
double.TryParse(price, NumberStyles.Currency, CultureInfo.InvariantCulture, out priceD);

This is what I came up with
double FixUnknownCurrency(string amountText)
{
amountText = amountText?.Trim()?.Replace(" ", string.Empty);
if(string.IsNullOrWhiteSpace(amountText))
return 0d;
if(amountText.Length < 3)
return double.Parse(amountText);
var currencyDecimal = amountText[amountText.Length-3];
if(Char.IsNumber(currencyDecimal))
return double.Parse(amountText);
if(currencyDecimal == '.' || currencyDecimal == ',')
{
amountText = amountText.Replace(",", string.Empty).Replace(".", string.Empty);
return double.Parse(amountText) / 100d;
}
return double.Parse(amountText);
}
Try it out here. Running late for train: https://dotnetfiddle.net/HTiL4s

I'm not aware of a better way other than trying different cultures to see what succeeds.
Create a method like this:
public double CurrencyToDouble(string input)
{
// Will hold the result
double result = 0;
// Try US culture
if(double.TryParse(input, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.GetCultureInfo("en-US"), out result)) { return result; }
// Try DE culture
if (double.TryParse(input, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.GetCultureInfo("de-DE"), out result)) { return result; }
// Try current system culture
if(double.TryParse(input, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.CurrentCulture, out result)) { return result; }
// Try invariant culture
if(double.TryParse(input, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.InvariantCulture, out result)) { return result; }
// Try more common cultures...
// Unsuccessful, throw some kind of error
throw new FormatException("Could not convert!");
}
Input:
CurrencyToDouble("2000,0.20");
CurrencyToDouble("2000.0,20");
Output:
20000.2
20000.2

Related

Converting to decimal returns the numbers concated [duplicate]

I want to parse a string like "3.5" to a double. However,
double.Parse("3.5")
yields 35 and
double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint)
throws a FormatException.
Now my computer's locale is set to German, wherein a comma is used as decimal separator. It might have to do something with that and double.Parse() expecting "3,5" as input, but I'm not sure.
How can I parse a string containing a decimal number that may or may not be formatted as specified in my current locale?
double.Parse("3.5", CultureInfo.InvariantCulture)
I usualy use a multi-culture function to parse user input, mostly because if someone is used to the numpad and is using a culture that use a comma as the decimal separator, that person will use the point of the numpad instead of a comma.
public static double GetDouble(string value, double defaultValue)
{
double result;
//Try parsing in the current culture
if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
//Then try in US english
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
//Then in neutral language
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
{
result = defaultValue;
}
return result;
}
Beware though, #nikie comments are true. To my defense, I use this function in a controlled environment where I know that the culture can either be en-US, en-CA or fr-CA. I use this function because in French, we use the comma as a decimal separator, but anybody who ever worked in finance will always use the decimal separator on the numpad, but this is a point, not a comma. So even in the fr-CA culture, I need to parse number that will have a point as the decimal separator.
I couldn't write a comment, so I write here:
double.Parse("3.5", CultureInfo.InvariantCulture) is not a good idea, because in Canada we write 3,5 instead of 3.5 and this function gives us 35 as a result.
I tested both on my computer:
double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK
This is a correct way that Pierre-Alain Vigeant mentioned
public static double GetDouble(string value, double defaultValue)
{
double result;
// Try parsing in the current culture
if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
// Then try in US english
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
// Then in neutral language
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
{
result = defaultValue;
}
return result;
}
Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)
Replace the comma with a point before parsing. Useful in countries with a comma as decimal separator. Think about limiting user input (if necessary) to one comma or point.
Look, every answer above that proposes writing a string replacement by a constant string can only be wrong. Why? Because you don't respect the region settings of Windows! Windows assures the user to have the freedom to set whatever separator character s/he wants. S/He can open up the control panel, go into the region panel, click on advanced and change the character at any time. Even during your program run. Think of this. A good solution must be aware of this.
So, first you will have to ask yourself, where this number is coming from, that you want to parse. If it's coming from input in the .NET Framework no problem, because it will be in the same format. But maybe it was coming from outside, maybe from a external server, maybe from an old DB that only supports string properties. There, the db admin should have given a rule in which format the numbers are to be stored. If you know for example that it will be an US DB with US format you can use this piece of code:
CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);
This will work fine anywhere on the world. And please don't use 'Convert.ToXxxx'. The 'Convert' class is thought only as a base for conversions in any direction. Besides: You may use the similar mechanism for DateTimes too.
The trick is to use invariant culture, to parse dot in all cultures.
double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);
string testString1 = "2,457";
string testString2 = "2.457";
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];
Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
My two cents on this topic, trying to provide a generic, double conversion method:
private static double ParseDouble(object value)
{
double result;
string doubleAsString = value.ToString();
IEnumerable<char> doubleAsCharList = doubleAsString.ToList();
if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
{
double.TryParse(doubleAsString.Replace(',', '.'),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else
{
if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
&& doubleAsCharList.Where(ch => ch == ',').Count() > 1)
{
double.TryParse(doubleAsString.Replace(",", string.Empty),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
&& doubleAsCharList.Where(ch => ch == '.').Count() > 1)
{
double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else
{
throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
}
}
return result;
}
Works as expected with:
1.1
1,1
1,000,000,000
1.000.000.000
1,000,000,000.99
1.000.000.000,99
5,000,111.3
5.000.111,3
0.99,000,111,88
0,99.000.111.88
No default conversion is implemented, so it would fail trying to parse 1.3,14, 1,3.14 or similar cases.
The following code does the job in any scenario. It's a little bit parsing.
List<string> inputs = new List<string>()
{
"1.234.567,89",
"1 234 567,89",
"1 234 567.89",
"1,234,567.89",
"123456789",
"1234567,89",
"1234567.89",
};
string output;
foreach (string input in inputs)
{
// Unify string (no spaces, only .)
output = input.Trim().Replace(" ", "").Replace(",", ".");
// Split it on points
string[] split = output.Split('.');
if (split.Count() > 1)
{
// Take all parts except last
output = string.Join("", split.Take(split.Count()-1).ToArray());
// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}
// Parse double invariant
double d = double.Parse(output, CultureInfo.InvariantCulture);
Console.WriteLine(d);
}
I think 100% correct conversion isn't possible, if the value comes from a user input. e.g. if the value is 123.456, it can be a grouping or it can be a decimal point. If you really need 100% you have to describe your format and throw an exception if it is not correct.
But I improved the code of JanW, so we get a little bit more ahead to the 100%. The idea behind is, that if the last separator is a groupSeperator, this would be more an integer type, than a double.
The added code is in the first if of GetDouble.
void Main()
{
List<string> inputs = new List<string>() {
"1.234.567,89",
"1 234 567,89",
"1 234 567.89",
"1,234,567.89",
"1234567,89",
"1234567.89",
"123456789",
"123.456.789",
"123,456,789,"
};
foreach(string input in inputs) {
Console.WriteLine(GetDouble(input,0d));
}
}
public static double GetDouble(string value, double defaultValue) {
double result;
string output;
// Check if last seperator==groupSeperator
string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
if (value.LastIndexOf(groupSep) + 4 == value.Count())
{
bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
result = tryParse ? result : defaultValue;
}
else
{
// Unify string (no spaces, only . )
output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");
// Split it on points
string[] split = output.Split('.');
if (split.Count() > 1)
{
// Take all parts except last
output = string.Join(string.Empty, split.Take(split.Count()-1).ToArray());
// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}
// Parse double invariant
result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
}
return result;
}
var doublePattern = #"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
var sourceDoubleString = "03444,44426";
var match = Regex.Match(sourceDoubleString, doublePattern);
var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);
Instead of having to specify a locale in all parses, I prefer to set an application wide locale, although if string formats are not consistent across the app, this might not work.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");
Defining this at the begining of your application will make all double parses expect a comma as the decimal delimiter. You can set an appropriate locale so that the decimal and thousands separator fits the strings you are parsing.
It's difficult without specifying what decimal separator to look for, but if you do, this is what I'm using:
public static double Parse(string str, char decimalSep)
{
string s = GetInvariantParseString(str, decimalSep);
return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
}
public static bool TryParse(string str, char decimalSep, out double result)
{
// NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
}
private static string GetInvariantParseString(string str, char decimalSep)
{
str = str.Replace(" ", "");
if (decimalSep != '.')
str = SwapChar(str, decimalSep, '.');
return str;
}
public static string SwapChar(string value, char from, char to)
{
if (value == null)
throw new ArgumentNullException("value");
StringBuilder builder = new StringBuilder();
foreach (var item in value)
{
char c = item;
if (c == from)
c = to;
else if (c == to)
c = from;
builder.Append(c);
}
return builder.ToString();
}
private static void ParseTestErr(string p, char p_2)
{
double res;
bool b = TryParse(p, p_2, out res);
if (b)
throw new Exception();
}
private static void ParseTest(double p, string p_2, char p_3)
{
double d = Parse(p_2, p_3);
if (d != p)
throw new Exception();
}
static void Main(string[] args)
{
ParseTest(100100100.100, "100.100.100,100", ',');
ParseTest(100100100.100, "100,100,100.100", '.');
ParseTest(100100100100, "100.100.100.100", ',');
ParseTest(100100100100, "100,100,100,100", '.');
ParseTestErr("100,100,100,100", ',');
ParseTestErr("100.100.100.100", '.');
ParseTest(100100100100, "100 100 100 100.0", '.');
ParseTest(100100100.100, "100 100 100.100", '.');
ParseTest(100100100.100, "100 100 100,100", ',');
ParseTest(100100100100, "100 100 100,100", '.');
ParseTest(1234567.89, "1.234.567,89", ',');
ParseTest(1234567.89, "1 234 567,89", ',');
ParseTest(1234567.89, "1 234 567.89", '.');
ParseTest(1234567.89, "1,234,567.89", '.');
ParseTest(1234567.89, "1234567,89", ',');
ParseTest(1234567.89, "1234567.89", '.');
ParseTest(123456789, "123456789", '.');
ParseTest(123456789, "123456789", ',');
ParseTest(123456789, "123.456.789", ',');
ParseTest(1234567890, "1.234.567.890", ',');
}
This should work with any culture. It correctly fails to parse strings that has more than one decimal separator, unlike implementations that replace instead of swap.
I improved the code of #JanW as well...
I need it to format results from medical instruments, and they also send ">1000", "23.3e02", "350E-02", and "NEGATIVE".
private string FormatResult(string vResult)
{
string output;
string input = vResult;
// Unify string (no spaces, only .)
output = input.Trim().Replace(" ", "").Replace(",", ".");
// Split it on points
string[] split = output.Split('.');
if (split.Count() > 1)
{
// Take all parts except last
output = string.Join("", split.Take(split.Count() - 1).ToArray());
// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}
string sfirst = output.Substring(0, 1);
try
{
if (sfirst == "<" || sfirst == ">")
{
output = output.Replace(sfirst, "");
double res = Double.Parse(output);
return String.Format("{1}{0:0.####}", res, sfirst);
}
else
{
double res = Double.Parse(output);
return String.Format("{0:0.####}", res);
}
}
catch
{
return output;
}
}
Here is a solution that handles any number string that many include commas and periods. This solution is particular for money amounts so only the tenths and hundredths place are expected. Anything more is treated as a whole number.
First remove anything that is not a number, comma, period, or negative sign.
string stringAmount = Regex.Replace(originalString, #"[^0-9\.\-,]", "");
Then we split up the number into the whole number and decimal number.
string[] decimalParsed = Regex.Split(stringAmount, #"(?:\.|,)(?=\d{2}$)");
(This Regex expression selects a comma or period that is two numbers from the end of the string.)
Now we take the whole number and strip it of any commas and periods.
string wholeAmount = decimalParsed[0].Replace(",", "").Replace(".", "");
if (wholeAmount.IsNullOrEmpty())
wholeAmount = "0";
Now we handle the decimal part, if any.
string decimalAmount = "00";
if (decimalParsed.Length == 2)
{
decimalAmount = decimalParsed[1];
}
Finally we can put the whole and decimal together and parse the Double.
double amount = $"{wholeAmount}.{decimalAmount}".ToDouble();
This will handle 200,00, 1 000,00 , 1,000 , 1.000,33 , 2,000.000,78 etc.
I am developing a .Net Maui app that runs on Windows, Mac, Android, and iPhone. I have 3 double values that I parse and store using '.' (e.g. "32.5") in all cases: latitude, longitude, and altitude. I happen to have the Android and iPhone set for Spanish and noticed that the Android parsed the '.' string just fine. However, the iPhone refused to parse it correctly unless I substituted ',' for the '.'. Otherwise, the result was always a huge number.
Rather than deal with the complications of localization, I came up with a simple solution that takes advantage of the specific limits of my double numbers.
case "Lat":
waypoint.Lat = ParseDouble(xmlVal, 90);
break;
case "Lon":
waypoint.Lon = ParseDouble(xmlVal, 180);
break;
case "Alt":
waypoint.Alt = ParseDouble(xmlVal, 32000);
public static double ParseDouble(string val, double limit)
{
double result;
if (double.TryParse(val, out result))
{
if (Math.Abs(result) <= limit)
return result;
else if (double.TryParse(val.Replace('.', ','), out result))
{
if (Math.Abs(result) <= limit)
return result;
}
}
return 0;
}
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;
string _pos = dblstr.Replace(".",
ci.NumberFormat.NumberDecimalSeparator).Replace(",",
ci.NumberFormat.NumberDecimalSeparator);
double _dbl = double.Parse(_pos);
The below is less efficient, but I use this logic. This is valid only if you have two digits after decimal point.
double val;
if (temp.Text.Split('.').Length > 1)
{
val = double.Parse(temp.Text.Split('.')[0]);
if (temp.Text.Split('.')[1].Length == 1)
val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
else
val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
val = double.Parse(RR(temp.Text));
Multiply the number and then divide it by what you multiplied it by before.
For example,
perc = double.Parse("3.555)*1000;
result = perc/1000

Code not converting text

I have a textbox that allows users to input a decimal values that then gets stored in the a table in the database, this piece of code works in the development environment. I have now published the my project to the server and now is not longer taking the values with the decimal places.
decimal ReceiptAmount;
decimal AmountDue;
decimal Change;
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text.Contains(".") == true)
{
ReceiptAmount = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text.Replace(".", ","));
}
else
{
ReceiptAmount = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text);
}
}
else
{
ReceiptAmount = 0;
}
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_AmountDue")).Text.Contains(".") == true)
{
AmountDue = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text.Replace(".", ","));
}
else
{
AmountDue = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text);
}
}
else
{
AmountDue = 0;
}
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_Change")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_Change")).Text.Contains(".") == true)
{
Change = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_Change")).Text.Replace(".", ","));
}
else
{
Change = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_Change")).Text);
}
}
else
{
Change = 0;
}
I am not to sure what seems to be the problem with this piece of code. The Textbox are found in a datalist that I loop through to get all of the values.
The Convert.ToDecimal overload that takes a string as input will parse the string using the CultureInfo.CurrentCulture. Probably your server has different regional settings. Depending on regional settings, a comma or point may be either interpreted as a thousand separator (and thus ignored) or as the decimal separator.
Instead, you should use Decimal.Parse directly, providing either a specific culture or the invariant culture, depending on your use case.
Ideally, you'd set the culture of the user somewhere. To achieve this there are multiple approaches, e.g. for ASP.Net Web forms: https://msdn.microsoft.com/en-us/library/bz9tc508.aspx
If you parse the string using the correct culture, you can get rid of the string manipulation for replacing . with ,.
First of all, lines like
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text))
look very ugly; let's extract a method (copy/paste is very, very bad practice):
private String FindDLText(String controlName) {
var box = dl_Item.FindControl(controlName) as TextBox;
return box == null ? null : box.Text;
}
Then you don't need checking Text.Contains(".") == true, just Replace if you really need it:
private Decimal FindDLValue(String controlName) {
String text = FindDLText(controlName);
if (String.IsNullOrEmpty(text))
return 0.0M;
//TODO: check if you really need this
//text = text.Replace(".", ",");
// you have to specify Culture either InvariantCulture or some predefined one;
// say, new CultureInfo("ru-RU") // <- use Russian Culture to parse this
return Decimal.Parse(text, CultureInfo.InvariantCulture);
}
Finally, you can get
decimal ReceiptAmount = FindDLValue("tb_ReceiptAmount");
decimal AmountDue = FindDLValue("tb_AmountDue");
decimal Change = FindDLValue("tb_Change");
feel the difference: three evident lines and two simple methods.

Convert currency string to decimal?

Objective
Sort a string that is displaying currency data like this $1,995.94 numerically in a set of data.
Code
I'm currently using the below code sample to convert the string value to decimal so that I can sort it properly.
if (sortBy == "checkAmount")
{
StringBuilder sb = new StringBuilder();
foreach (var c in Convert.ToString(p.GetType().GetProperty(sortBy).GetValue(p, null)))
{
if (!char.IsDigit(c) && c != '.') { continue; }
sb.Append(c);
}
return Convert.ToDecimal(sb.ToString());
}
else
{
return p.GetType().GetProperty(sortBy).GetValue(p, null);
}
Problem
What's a better way of doing this? It works, and that's cool, but it's not very elegant.
Final Solution
The answer provided by Servy works as expected, and I used that implementation for a while, but a colleague and I found an even better way so I'm documenting it here. BTW, I ended up using this solution in the end.
decimal.Parse(input, NumberStyles.AllowCurrencySymbol | NumberStyles.Number);
How about this, but only works for one string value. So you need to get your string split by $ and then do the conversion while saving into the array or list
using System.Globalization;
//rest of your code
string str = "$50,550.20";
decimal decval;
bool convt = decimal.TryParse(str, NumberStyles.Currency,
CultureInfo.CurrentCulture.NumberFormat, out decval);
if (convt)
Console.WriteLine(decval);
Console.ReadLine();
Here is a simpler solution:
public static decimal ToDecimal(this string str)
{
return decimal.Parse(str, NumberStyles.Currency);
}
and the unit test:
[Test]
public void ToDecimal_Convert_String_To_Decimal()
{
Assert.AreEqual(1234M, "1234".ToDecimal());
Assert.AreEqual(-1234.56M, "$(1,234.56)".ToDecimal());
Assert.AreEqual(1234.56M, "$1,234.56".ToDecimal());
}
Here is a method that most closely resembles the code you've provided
public static decimal Parse(string input)
{
return decimal.Parse(Regex.Replace(input, #"[^\d.]", ""));
}
Here is an option that will support negative numbers, and will stop if it finds a second period value, thus reducing the number of strings it returns that are not valid decimal values. It also has a few other modifications not seen in the OP to handle additional cases your current code doesn't.
public static decimal Parse(string input)
{
return decimal.Parse(Regex.Match(input, #"-?\d{1,3}(,\d{3})*(\.\d+)?").Value);
}
decimal amount = decimal.Parse("$123,456.78",
NumberStyles.AllowCurrencySymbol |
NumberStyles.AllowThousands |
NumberStyles.AllowDecimalPoint);
works for all culture:
var d = decimal.Parse("$497.7", NumberStyles.Currency, CultureInfo.CreateSpecificCulture("us-US").NumberFormat);
Console.WriteLine(d);
public static decimal ToDecimalFromStringDecimalOrMoneyFormattedDecimal(this string s)
{
try
{
return decimal.Parse(s);
}
catch
{
var numberWithoutMoneyFormatting = Regex.Replace(s, #"[^\d.-]", "");
return decimal.Parse(numberWithoutMoneyFormatting);
}
}
[Test]
public void Test_ToDecimalFromStringDecimalOrMoneyFormattedDecimal()
{
Assert.That("$ 500".ToDecimalFromStringDecimalOrMoneyFormattedDecimal() == (decimal)500);
Assert.That("R -500".ToDecimalFromStringDecimalOrMoneyFormattedDecimal() == (decimal)-500);
Assert.That("-$ 500".ToDecimalFromStringDecimalOrMoneyFormattedDecimal() == (decimal)-500);
Assert.That("P 500.90".ToDecimalFromStringDecimalOrMoneyFormattedDecimal() == (decimal)500.9);
Assert.That("$ -50 0,090,08.08".ToDecimalFromStringDecimalOrMoneyFormattedDecimal() == (decimal)-50009008.08);
}
We can try to use decimal.TryParse to cast the currency string value to a decimal number.
NumberStyles:use NumberStyles.Currency which indicates that all styles except AllowExponent and AllowHexSpecifier are used. This is a composite number style.
CultureInfo: we need to set the right culture of currency which align with your currency value.
For this example, $20,000.00 is USA currency we can try to use English (USA) for that, on the other hand 20,000.00€ is euro currency we can try to use English (Ireland) that uses the euro currency.
decimal.TryParse("20,000.00€", NumberStyles.Currency ,CultureInfo.CreateSpecificCulture("en-IE"), out var euro);
Console.WriteLine(euro);
decimal.TryParse("$20,000.00", NumberStyles.Currency ,CultureInfo.CreateSpecificCulture("en-us"), out var dollar);
Console.WriteLine(dollar);
c# online

Converting string to double in C#

I have a long string with double-type values separated by # -value1#value2#value3# etc
I splitted it to string table. Then, I want to convert every single element from this table to double type and I get an error. What is wrong with type-conversion here?
string a = "52.8725945#18.69872650000002#50.9028073#14.971600200000012#51.260062#15.5859949000000662452.23862099999999#19.372202799999250800000045#51.7808372#19.474096499999973#";
string[] someArray = a.Split(new char[] { '#' });
for (int i = 0; i < someArray.Length; i++)
{
Console.WriteLine(someArray[i]); // correct value
Convert.ToDouble(someArray[i]); // error
}
There are 3 problems.
1) Incorrect decimal separator
Different cultures use different decimal separators (namely , and .).
If you replace . with , it should work as expected:
Console.WriteLine(Convert.ToDouble("52,8725945"));
You can parse your doubles using overloaded method which takes culture as a second parameter. In this case you can use InvariantCulture (What is the invariant culture) e.g. using double.Parse:
double.Parse("52.8725945", System.Globalization.CultureInfo.InvariantCulture);
You should also take a look at double.TryParse, you can use it with many options and it is especially useful to check wheter or not your string is a valid double.
2) You have an incorrect double
One of your values is incorrect, because it contains two dots:
15.5859949000000662452.23862099999999
3) Your array has an empty value at the end, which is an incorrect double
You can use overloaded Split which removes empty values:
string[] someArray = a.Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
Add a class as Public and use it very easily like convertToInt32()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Common
/// </summary>
public static class Common
{
public static double ConvertToDouble(string Value) {
if (Value == null) {
return 0;
}
else {
double OutVal;
double.TryParse(Value, out OutVal);
if (double.IsNaN(OutVal) || double.IsInfinity(OutVal)) {
return 0;
}
return OutVal;
}
}
}
Then Call The Function
double DirectExpense = Common.ConvertToDouble(dr["DrAmount"].ToString());
Most people already tried to answer your questions.
If you are still debugging, have you thought about using:
Double.TryParse(String, Double);
This will help you in determining what is wrong in each of the string first before you do the actual parsing.
If you have a culture-related problem, you might consider using:
Double.TryParse(String, NumberStyles, IFormatProvider, Double);
This http://msdn.microsoft.com/en-us/library/system.double.tryparse.aspx has a really good example on how to use them.
If you need a long, Int64.TryParse is also available: http://msdn.microsoft.com/en-us/library/system.int64.tryparse.aspx
Hope that helps.
private double ConvertToDouble(string s)
{
char systemSeparator = Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator[0];
double result = 0;
try
{
if (s != null)
if (!s.Contains(","))
result = double.Parse(s, CultureInfo.InvariantCulture);
else
result = Convert.ToDouble(s.Replace(".", systemSeparator.ToString()).Replace(",", systemSeparator.ToString()));
}
catch (Exception e)
{
try
{
result = Convert.ToDouble(s);
}
catch
{
try
{
result = Convert.ToDouble(s.Replace(",", ";").Replace(".", ",").Replace(";", "."));
}
catch {
throw new Exception("Wrong string-to-double format");
}
}
}
return result;
}
and successfully passed tests are:
Debug.Assert(ConvertToDouble("1.000.007") == 1000007.00);
Debug.Assert(ConvertToDouble("1.000.007,00") == 1000007.00);
Debug.Assert(ConvertToDouble("1.000,07") == 1000.07);
Debug.Assert(ConvertToDouble("1,000,007") == 1000007.00);
Debug.Assert(ConvertToDouble("1,000,000.07") == 1000000.07);
Debug.Assert(ConvertToDouble("1,007") == 1.007);
Debug.Assert(ConvertToDouble("1.07") == 1.07);
Debug.Assert(ConvertToDouble("1.007") == 1007.00);
Debug.Assert(ConvertToDouble("1.000.007E-08") == 0.07);
Debug.Assert(ConvertToDouble("1,000,007E-08") == 0.07);
In your string I see: 15.5859949000000662452.23862099999999 which is not a double (it has two decimal points). Perhaps it's just a legitimate input error?
You may also want to figure out if your last String will be empty, and account for that situation.

C# Double - ToString() formatting with two decimal places but no rounding

How do I format a Double to a String in C# so as to have only two decimal places?
If I use String.Format("{0:0.00}%", myDoubleValue) the number is then rounded and I want a simple truncate without any rounding. I also want the conversion to String to be culture sensitive.
I use the following:
double x = Math.Truncate(myDoubleValue * 100) / 100;
For instance:
If the number is 50.947563 and you use the following, the following will happen:
- Math.Truncate(50.947563 * 100) / 100;
- Math.Truncate(5094.7563) / 100;
- 5094 / 100
- 50.94
And there's your answer truncated, now to format the string simply do the following:
string s = string.Format("{0:N2}%", x); // No fear of rounding and takes the default number format
The following rounds the numbers, but only shows up to 2 decimal places (removing any trailing zeros), thanks to .##.
decimal d0 = 24.154m;
decimal d1 = 24.155m;
decimal d2 = 24.1m;
decimal d3 = 24.0m;
d0.ToString("0.##"); //24.15
d1.ToString("0.##"); //24.16 (rounded up)
d2.ToString("0.##"); //24.1
d3.ToString("0.##"); //24
http://dobrzanski.net/2009/05/14/c-decimaltostring-and-how-to-get-rid-of-trailing-zeros/
I suggest you truncate first, and then format:
double a = 123.4567;
double aTruncated = Math.Truncate(a * 100) / 100;
CultureInfo ci = new CultureInfo("de-DE");
string s = string.Format(ci, "{0:0.00}%", aTruncated);
Use the constant 100 for 2 digits truncate; use a 1 followed by as many zeros as digits after the decimal point you would like. Use the culture name you need to adjust the formatting result.
i use price.ToString("0.00")
for getting the leading 0s
Simplest method, use numeric format strings:
double total = "43.257"
MessageBox.Show(total.ToString("F"));
The c# function, as expressed by Kyle Rozendo:
string DecimalPlaceNoRounding(double d, int decimalPlaces = 2)
{
double factor = Math.Pow(10, decimalPlaces);
d = d * factor;
d = Math.Truncate(d);
d = d / factor;
return string.Format("{0:N" + Math.Abs(decimalPlaces) + "}", d);
}
How about adding one extra decimal that is to be rounded and then discarded:
var d = 0.241534545765;
var result1 = d.ToString("0.###%");
var result2 = result1.Remove(result1.Length - 1);
I had that problem with Xamarin Forms and solved it with this:
percent.ToString("0.##"+"%")
This is working for me
string prouctPrice = Convert.ToDecimal(String.Format("{0:0.00}", Convert.ToDecimal(yourString))).ToString();
I know this is a old thread but I've just had to do this. While the other approaches here work, I wanted an easy way to be able to affect a lot of calls to string.format. So adding the Math.Truncate to all the calls to wasn't really a good option. Also as some of the formatting is stored in a database, it made it even worse.
Thus, I made a custom format provider which would allow me to add truncation to the formatting string, eg:
string.format(new FormatProvider(), "{0:T}", 1.1299); // 1.12
string.format(new FormatProvider(), "{0:T(3)", 1.12399); // 1.123
string.format(new FormatProvider(), "{0:T(1)0,000.0", 1000.9999); // 1,000.9
The implementation is pretty simple and is easily extendible to other requirements.
public class FormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof (ICustomFormatter))
{
return this;
}
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (arg == null || arg.GetType() != typeof (double))
{
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
if (format.StartsWith("T"))
{
int dp = 2;
int idx = 1;
if (format.Length > 1)
{
if (format[1] == '(')
{
int closeIdx = format.IndexOf(')');
if (closeIdx > 0)
{
if (int.TryParse(format.Substring(2, closeIdx - 2), out dp))
{
idx = closeIdx + 1;
}
}
else
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
}
double mult = Math.Pow(10, dp);
arg = Math.Truncate((double)arg * mult) / mult;
format = format.Substring(idx);
}
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
private string HandleOtherFormats(string format, object arg)
{
if (arg is IFormattable)
{
return ((IFormattable) arg).ToString(format, CultureInfo.CurrentCulture);
}
return arg != null ? arg.ToString() : String.Empty;
}
}
To what is worth, for showing currency, you can use "C":
double cost = 1.99;
m_CostText.text = cost.ToString("C"); /*C: format as currentcy */
Output: $1.99
You could also write your own IFormatProvider, though I suppose eventually you'd have to think of a way to do the actual truncation.
The .NET Framework also supports custom formatting. This typically involves the creation of a formatting class that implements both IFormatProvider and ICustomFormatter. (msdn)
At least it would be easily reusable.
There is an article about how to implement your own IFormatProvider/ICustomFormatter here at CodeProject. In this case, "extending" an existing numeric format might be the best bet. It doesn't look too hard.
Following can be used for display only which uses the property of String ..
double value = 123.456789;
String.Format("{0:0.00}", value);
Solution:
var d = 0.123345678;
var stringD = d.ToString();
int indexOfP = stringD.IndexOf(".");
var result = stringD.Remove((indexOfP+1)+2);
(indexOfP+1)+2(this number depend on how many number you want to
preserve. I give it two because the question owner want.)
Also note the CultureInformation of your system. Here my solution without rounding.
In this example you just have to define the variable MyValue as double.
As result you get your formatted value in the string variable NewValue.
Note - Also set the C# using statement:
using System.Globalization;
string MyFormat = "0";
if (MyValue.ToString (CultureInfo.InvariantCulture).Contains (CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
{
MyFormat += ".00";
}
string NewValue = MyValue.ToString(MyFormat);

Categories