String.Format Currency for K, M and B - c#

If a currency amount is very large, I'm trying to abbreviate it.
For example:
if (amt > 1000000)
{
decimal d = (decimal)Math.Round(amt / 1000, 0);
return String.Format("{0:C0}", d) + " K";
}
If a number is given over 1 million, it will take off the last 3 digits and replace with a K. Works just fine when the currency symbol (like $ is on the left hand side)
However, some currency symbols get put on the right hand side.
So instead of a nice looking $100 K for USD, I'd get 100 € K for French Euros.
How can I change the format to put the K immediately after the numbers, and before the currency symbol.
Seems like it might be a step too far. Any ideas?

I would create a class with the IFormatProvider like this
public class MoneyFormat: IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string fmt, object arg, IFormatProvider formatProvider)
{
if (arg.GetType() != typeof(decimal))
try
{
return HandleOtherFormats(fmt, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid", fmt), e);
}
string ufmt = fmt.ToUpper(CultureInfo.InvariantCulture);
if (!(ufmt == "K"))
try
{
return HandleOtherFormats(fmt, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid", fmt), e);
}
decimal result;
if (decimal.TryParse(arg.ToString(), out result))
{
if (result >= 1000000)
{
decimal d = (decimal)Math.Round(result / 10000, 0);
CultureInfo clone = (CultureInfo)CultureInfo.CurrentCulture.Clone();
string oldCurrSymbol = clone.NumberFormat.CurrencySymbol;
clone.NumberFormat.CurrencySymbol = "";
return String.Format(clone, "{0:C0}", d).Trim() + " K" + oldCurrSymbol;
}
}
else
return string.Format("{0:C0}", result) + " K";
}
private string HandleOtherFormats(string format, object arg)
{
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
else if (arg != null)
return arg.ToString();
else
return string.Empty;
}
}
Then you can call it in your format like so:
return string.Format( new MoneyFormat(), "{0:K}", amt);
You can then tweek the way you want to represent your "K" or other reference symbols that you want to add
CultureInfo("fr-fr") : 100 K€
CultureInfo("en-us") : 100 K$
CultureInfo("ru-RU") : 100 Kр.

You can use the CurrencyPositivePattern to determine if the currency symbol comes before or after the number. Then you can modify the CurrencySymbol to suit your needs.
decimal amt = 10000000;
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //set up France as current culture
NumberFormatInfo NFI = CultureInfo.CurrentCulture.NumberFormat;
string currencySymbol = NFI.CurrencySymbol;
int currencyPosition = NFI.CurrencyPositivePattern;
if (amt > 1000000)
{
if (currencyPosition == 3) // n $
{
NFI.CurrencySymbol = "K " + currencySymbol;
}
decimal d = (decimal)Math.Round(amt / 1000, 0);
string output = d.ToString("c");
}
I know this is not the best implementation of a custom number format, but this is just to get the idea across.
See:
NumberFormatInfo.CurrencyPositivePattern Property

Related

c# Why do I get the double value without decimal (Conversion from object to double) [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

string to decimal using extension methods

My string is 1799.00
I want to like this: 1.799,00
But i cannot convert this method.
I'm using this function.
public static decimal ToDecimal(this object str)
{
if (str != null)
{
try
{
return Convert.ToDecimal(str, new CultureInfo("tr-TR"));
}
catch (Exception)
{
return 0;
}
}
else
{
return 0;
}
}
You are, probably, looking for changing formats. Given a decimal as a string in invariant culture representation ("1799.00") you want a string, but in Turkish cutrure representation: ("1.799,00")
// you want to return string in Turkish culture, right?
public static string ToTuskishDecimal(this object value) {
if (null == value)
return null; // or throw exception, or return "0,00" or return "?"
try {
return Convert
.ToDecimal(value, CultureInfo.InvariantCulture)
.ToString("N", CultureInfo.GetCultureInfo("tr-TR"));
}
catch (FormatException) {
return "0,00"; // or "?"
}
}
Test:
decimal d = 1799.00m;
string s = d.ToString(CultureInfo.InvariantCulture);
// 1.799,00
Console.Write(d.ToTuskishDecimal());
// 1.799,00
Console.Write(s.ToTuskishDecimal());
In case you want to return decimal you have to format it manually when printing out:
public static decimal ToDecimal(this object value) {
if (null == value)
return 0.00m;
try {
return Convert.ToDecimal(value, CultureInfo.InvariantCulture);
}
catch (FormatException) {
return 0.00m;
}
}
...
// return me a decimal, please
decimal d = "1799.00".ToDecimal();
// when printing decimal, use Turkish culture
Console.Write(d.ToString("N", CultureInfo.GetCultureInfo("tr-TR")));
you can specify Turkish culture as a default one for the entire thread:
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
...
// now you don't have to mention Turkish culture
// ...but still have to specify the format
Console.Write(d.ToString("N"));
I use this to obtain a decimal value:
public static decimal toDecimal( string s ) {
decimal res = 0;
decimal.TryParse(s.Replace('.', ','), out res);
return res;
}
In case you want to show the decimal value on the format you ask, try this:
toDecimal("1700.99").ToString("N2")
you will obtain the "1.700,99" string.
I wrote another simple example to obtain a decimal value in Turkish format:
decimal value = 1700.00m;
Console.WriteLine(value.ToString("N", CultureInfo.GetCultureInfo("tr-TR")));

Parse value with Currency symbol

I have looked to multiple SO questions on parsing currency, the best (recommended) way seems to be the one I'm trying below:
var payout = decimal.Parse("$2.10", NumberStyles.Currency | NumberStyles.AllowDecimalPoint);
However, it throws and exception: Input string is not in the correct format.
I don't know what I'm doing wrong?
EDIT
Thanks for the answers. Additional info: the hard-coded currency value I gave was just an example. I have a list of currencies:
€2,66
$2.10
$5.55
etc.
I cannot determine the culture info in advance. Any ideas?
Similar approach #un-lucky mentioned as one of the answer, I tried making it generic and work for every Symbol/Format
public static decimal ParseCurrencyWithSymbol(string input)
{
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures)
.GroupBy(c=> c.NumberFormat.CurrencySymbol)
.ToDictionary(c=> c.Key, c=>c.First());
var culture = cultures.FirstOrDefault(c=>input.Contains(c.Key));
decimal result = 0;
if(!culture.Equals(default(KeyValuePair<string,CultureInfo>)))
{
result = decimal.Parse(input, NumberStyles.Currency | NumberStyles.AllowDecimalPoint, culture.Value);
}
else
{
if( !decimal.TryParse(input, out result))
{
throw new Exception("Invalid number format");
}
}
return result;
}
Usage
decimal output = ParseCurrencyWithSymbol("$2.10");
Working Code
You can try like this:
decimal currencyValue;
string inputCurrency = "$12.6";
if (decimal.TryParse(inputCurrency, NumberStyles.Currency, CultureInfo.CreateSpecificCulture("en-US"), out currencyValue))
{
// proceed with currencyValue
}
else
{
//Show error ; Conversion failed
}
For dealing with all currencies you can use the following:
Dictionary<char, string> currencyCulture = new Dictionary<char, string>();
currencyCulture.Add('$', "en-US");
currencyCulture.Add('€', "en-IE");
// populate all posible values here
decimal currencyValue;
string inputCurrency = "€2,66";
char currencySymbol= inputCurrency.ToCharArray()[0];
CultureInfo currentCulture= CultureInfo.CreateSpecificCulture(currencyCulture[currencySymbol]);
if (decimal.TryParse(inputCurrency, NumberStyles.Currency, currentCulture, out currencyValue))
{
// proceed with currencyValue
}
else
{
//Show error ; Conversion failed
}
You can choose culture Names from here
Pertinent to your particular case, you may use the following code snippet:
var payout = decimal.Parse("$2.10".Replace("$",""));
If you don't know what the currency symbol would be, then try the following solution:
string _money = "$2.10";
var payout = decimal.Parse(_money.Substring(1));
Dealing with commas and decimal points is much more difficult: if this is the issue, refer to the solution given by member #un-lucky.
Hope this may help.
How about a CleanCurrency method?
/// Loops each char in the string and returns only numbers, . or ,
static double? CleanCurrency(string currencyStringIn) {
string temp = "";
int n;
for (int i = 0; i < currencyStringIn.Length; i++) {
string c = currencyStringIn.Substring(i, 1);
if (int.TryParse(c, out n) || c == "." || c == ",") {
temp += c;
}
}
if (temp == "") {
return null;
else {
return double.Parse("0" + temp);
}
}
The idea here being to just get an actual number regardless of what the string content is.
double? payout = CleanCurrency("$3.50");
I've expounded on Hari Prasad's answer. With this you can minimize the culture result. Update "SupportedCultures" with the ones you might use in your app.
private static readonly List<string> SupportedCultures = new List<string> {"en-US", "en-GB", "fr-FR"};
public static void Main()
{
var (amount, culture) = ParseCurrencyWithSymbol("$256.12");
Console.WriteLine($"{culture?.Name} | {amount}");
var (amount2, culture2) = ParseCurrencyWithSymbol("£389.17");
Console.WriteLine($"{culture2?.Name} | {amount2}");
var (amount3, culture3) = ParseCurrencyWithSymbol("€421,10");
Console.WriteLine(culture3 != null ? $"{culture3.Name} | {amount3}" : "Failed!");
}
public static Tuple<decimal?, CultureInfo> ParseCurrencyWithSymbol(string input)
{
var culture = CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(x => SupportedCultures.Contains(x.Name))
.FirstOrDefault(c => input.Contains(c.NumberFormat.CurrencySymbol));
if (culture == null) return new Tuple<decimal?, CultureInfo>(null, null);
return new Tuple<decimal?, CultureInfo>(decimal.Parse(input,
NumberStyles.Currency | NumberStyles.AllowCurrencySymbol |
NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, culture), culture);
}

C# How to format a double to one decimal place without rounding

I need to format a double value to one decimal place without it rounding.
double value = 3.984568438706
string result = "";
What I have tried is:
1)
result = value.ToString("##.##", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 3.98%
2)
result = value.ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4%
3)
result = value.ToString("##.0", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4.0%
4) (Following other suggestions)
value = (value / 100);
result = String.Format("{0:P1}", Math.Truncate(value * 10000) / 10000);
// returns 4.0%
result = string.Format("{0:0.0%}",value); // returns 4.0%
What I need to display is the value 3.9%
Thanks for any help in advance.
result=string.Format("{0:0.0}",Math.Truncate(value*10)/10);
I would make a utility method to handle this:
static double Truncate(double value, int digits)
{
double mult = System.Math.Pow(10.0, digits);
return System.Math.Truncate(value * mult) / mult;
}
You could then do:
result = Truncate(value, 1).ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
Note that you may also want Math.Floor instead of truncate - but it depends on how you want negative values handled.
I know this is a old thread but I've just had to do this. While the approaches here work I want a easy way to be able to affect a lot of calls so using the Math.Truncate on all the calls to string.format wasn't really a good option.
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.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;
}
}
ToString() doesn't do it. You have to add extra code. The other answers show math approaches, my approach below is kind of outside-the-box.
string result = value.ToString();
Console.WriteLine("{0}", result.Substring(0, result.LastIndexOf('.') + 2));
This is a fairly simple brute force approach, but it does the trick when the decimal is a '.'. Here's an extension method to ease the pain (and deals with the decimal point).
public static class Extensions
{
public static string ToStringNoTruncate(this double me, int decimalplaces = 1)
{
string result = me.ToString();
char dec = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
return result.Substring(0, result.LastIndexOf(dec) + decimalplaces + 1);
}
}
( Math.Truncate( ( value * 10 ) ) / 1000 ).ToString( "#.#%" )
Just use modulo operator + built in ToString:
result = (value - (value % 0.1)).ToString("N1") + "%";

How do I parse a string with a decimal point to a double?

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

Categories

Resources