Description of the Syntax within System.Data.DataTable.Compute - c#

First I have following numbers which I convert in a string.
decimal numberA = 1.0M;
decimal numberB = 2.0M;
string numberAString = numberA.ToString(); // numberAString = 1,0
string numberBString = numberB.ToString(); // numberBString = 2,0
So far so good ToString() uses the current culture, which is on my system de-CH and the decimal separator is a coma.
Then I create a formula expression and compute the formula with the Method System.Data.DataTable.Compute
// create formula expression
string formulaExpression = "{0} + {1}";
string formula = string.Format(formulaExpression, numberAString, numberBString);
// formula = 1,0 + 2,0
// compute formula
object result;
result = new System.Data.DataTable().Compute(formula, filter:string.Empty);
I got a System.Data.SyntaxErrorException when I run this code. I recognized, that the Compute method is not satisfy with the coma delimiter. When I convert the decimal value with .ToString(System.Globalization.CultureInfo.InvariantCulture) the culture-independent language english is used and therefore the point delimiter for the string representation of the decimal value is used and it works fine.
My question is, what is the name of this Syntax that I can use as expression within the System.Data.DataTable.Compute Method and where is this Syntax respectively the allowed operators and functions described? The MSDN Documentation gives me not so much information about this Syntax.

The Expression Syntax is described in the MSDN Documentation of the Property System.Data.DataColumn.Expression.
You can find this hint at the bottom of the section remarks from the MSDN documention of System.Data.DataTable.Compute.
Nice to know is the following statement within the section "Parsing Literal Expressions" within the Documentation of the Expression Syntax.
All literal expressions must be expressed in the invariant culture locale. When DataSet parses and converts literal expressions, it always uses the invariant culture, not the current culture.
So in my case I have to convert the decimals numberA and numberB with following ToString overload to consider the invariant culture locale.
string numberAString = numberA.ToString(System.Globalization.CultureInfo.InvariantCulture);
// numberAString = 1.0
string numberBString = numberB.ToString(System.Globalization.CultureInfo.InvariantCulture);
// numberBString = 2.0
A little bit special in my case is the circumstance that I run in a known Windows 8.1 Bug. For some reason the decimal delimiter is for the culture de-CH a coma. This is wrong. Correct would be a point as decimal delimiter character. For details refer this Wiki article.

Related

Convert.ToDecimal from input string ignores dots but accepts comma [duplicate]

This question already has answers here:
String to decimal conversion: dot separation instead of comma
(8 answers)
Closed 3 years ago.
I have this small piece of code:
using System;
using System.Globalization;
namespace project
{
class conditionalStatements
{
static void Main(string[] args)
{
Console.WriteLine("Enter a number greater than 45.2");
string answer = Console.ReadLine();
decimal answer_decimal = Convert.ToDecimal(answer);
// decimal answer_decimal = Decimal.Parse(answer);
decimal compareValue = 45.2m;
Console.WriteLine(answer_decimal);
//prints 452
if(Decimal.Compare(answer_decimal, compareValue) > 0){
// stuff
}
else{
// should enter here
}
}
}
}
The problem is that since both the method Convert.ToDecimal() and Decimal.Parse() ignore the dot notation of decimal values (or at least that's what's happening to me) the number is interpreted as 452 instead of 45.2. No matter how many dots I input. In fact, if I were to enter:
45......2
the converted value still would be converted to 452. Only if I use the comma, then the converted number is correctly interpreted as 45.2 and I am able to enter the else condition.
I did not change the NumberFormatInfo.CurrencyDecimalSeparator. I left it as default '.'
Convert input string to decimal with a given culture that treats dot as decimal separator:
decimal answer_decimal = Convert.ToDecimal(answer, new CultureInfo("en-US"));
You could try something like the code below:
decimal answer_decimal;
while(!decimal.TryParse(answer, out answer_decimal)){
Console.WriteLine("Value entered could not be converted.");
}
decimal compareValue = 45.2m;
Console.WriteLine(answer_decimal);
//prints 452
if(Decimal.Compare(answer_decimal, compareValue) > 0){
// stuff
}
else{
// should enter here
}
This way you prevent the program from crashing if the conversion is not possible.
If you want to use a specific culture you can do so by using an overload of decimal.TryParse as follows:
decimal.TryParse(answer, System.Globalization.NumberStyles.Any, new System.Globalization.CultureInfo("EN-us"), out answer_decimal);
Is your culture set to Dari?
I think the applicable NumberFormatInfo property would be NumberDecimalSeparator, not CurrencyDecimalSeparator. decimal.Parse(), called directly or via Convert.ToDecimal(), would have no idea currency is what's being parsed unless a NumberStyles value with one of the *Currency* flags were passed.
When an overload of decimal.Parse() is called that does not accept a NumberStyles parameter it defaults to NumberStyles.Number. This composite style includes the NumberStyles.AllowDecimalPoint style, the documentation for which states (emphasis mine)...
If the NumberStyles value includes the AllowCurrencySymbol flag and the parsed string includes a currency symbol, the decimal separator character is determined by the CurrencyDecimalSeparator property. Otherwise, the decimal separator character is determined by the NumberDecimalSeparator property.
Now, are there actually any cultures that use different decimal separators for numbers and currency? Let's find out...
CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(culture => culture.NumberFormat.CurrencyDecimalSeparator != culture.NumberFormat.NumberDecimalSeparator);
On .NET Framework v4.7.2 that yields a small number of cultures...
fr-CH
kea
kea-CV
mr
mr-IN
prs
prs-AF
pt-CV
Tweaking that LINQ query to account for the specific behavior you're seeing (currency decimal separator is ".", number decimal separator is ",", number group separator is ".")...
CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(culture => culture.NumberFormat.CurrencyDecimalSeparator == ".")
.Where(culture => culture.NumberFormat.NumberDecimalSeparator == ",")
.Where(culture => culture.NumberFormat.NumberGroupSeparator == ".");
...narrows it down to two Dari cultures...
prs
prs-AF
Sure enough, if I change my culture to Dari beforehand...
CultureInfo.CurrentCulture = new CultureInfo("prs");
...on my system your code behaves exactly as you described. If you don't want to use your culture's separators the solution, of course, is to specify at the system, thread, or method level a specific or custom culture with the separators you do want.

string format money , [duplicate]

I need to display a number with commas and a decimal point.
Eg:
Case 1 : Decimal number is 432324 (This does not have commas or decimal points).
Need to display it as: 432,324.00.
Not: 432,324
Case 2 : Decimal number is 2222222.22 (This does not have commas).
Need to display it as: 2,222,222.22
I tried ToString("#,##0.##"), but it is not formatting it correctly.
int number = 1234567890;
number.ToString("#,##0.00");
You will get the result 1,234,567,890.00.
Maybe you simply want the standard format string "N", as in
number.ToString("N")
It will use thousand separators, and a fixed number of fractional decimals. The symbol for thousands separators and the symbol for the decimal point depend on the format provider (typically CultureInfo) you use, as does the number of decimals (which will normally by 2, as you require).
If the format provider specifies a different number of decimals, and if you don't want to change the format provider, you can give the number of decimals after the N, as in .ToString("N2").
Edit: The sizes of the groups between the commas are governed by the
CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes
array, given that you don't specify a special format provider.
Try with
ToString("#,##0.00")
From MSDN
*The "0" custom format specifier serves as a zero-placeholder symbol. If the value that is being formatted has a digit in the position where the zero appears in the format string, that digit is copied to the result string; otherwise, a zero appears in the result string. The position of the leftmost zero before the decimal point and the rightmost zero after the decimal point determines the range of digits that are always present in the result string.
The "00" specifier causes the value to be rounded to the nearest digit preceding the decimal, where rounding away from zero is always used. For example, formatting 34.5 with "00" would result in the value 35.*
I had the same problem. I wanted to format numbers like the "General" format in spreadsheets, meaning show decimals if they're significant, but chop them off if not. In other words:
1234.56 => 1,234.56
1234 => 1,234
It needs to support a maximum number of places after the decimal, but don't put trailing zeros or dots if not required, and of course, it needs to be culture friendly. I never really figured out a clean way to do it using String.Format alone, but a combination of String.Format and Regex.Replace with some culture help from NumberFormatInfo.CurrentInfo did the job (LinqPad C# Program).
string FormatNumber<T>(T number, int maxDecimals = 4) {
return Regex.Replace(String.Format("{0:n" + maxDecimals + "}", number),
#"[" + System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator + "]?0+$", "");
}
void Main(){
foreach (var test in new[] { 123, 1234, 1234.56, 123456.789, 1234.56789123 } )
Console.WriteLine(test + " = " + FormatNumber(test));
}
Produces:
123 = 123
1234 = 1,234
1234.56 = 1,234.56
123456.789 = 123,456.789
1234.56789123 = 1,234.5679
Try with
ToString("#,##0.###")
Produces:
1234.55678 => 1,234.556
1234 => 1,234
For Razor View:
$#string.Format("{0:#,0.00}",item.TotalAmount)
CultureInfo us = new CultureInfo("en-US");
TotalAmount.ToString("N", us)
Your question is not very clear but this should achieve what you are trying to do:
decimal numericValue = 3494309432324.00m;
string formatted = numericValue.ToString("#,##0.00");
Then formatted will contain: 3,494,309,432,324.00
All that is needed is "#,0.00", c# does the rest.
Num.ToString("#,0.00"")
The "#,0" formats the thousand separators
"0.00" forces two decimal points
If you are using string variables you can format the string directly using a : then specify the format (e.g. N0, P2, etc).
decimal Number = 2000.55512016465m;
$"{Number:N}" #Outputs 2,000.55512016465
You can also specify the number of decimal places to show by adding a number to the end like
$"{Number:N1}" #Outputs 2,000.5
$"{Number:N2}" #Outputs 2,000.55
$"{Number:N3}" #Outputs 2,000.555
$"{Number:N4}" #Outputs 2,000.5551
string Mynewcurrency = DisplayIndianCurrency("7743450.00");
private string DisplayIndianCurrency(string EXruppesformate)
{
string fare = EXruppesformate;
decimal parsed = decimal.Parse(fare, CultureInfo.InvariantCulture);
CultureInfo hindi = new CultureInfo("en-IN");
// string text = string.Format(hindi, "{0:c}", parsed);if you want <b>Rs 77,43,450.00</b>
string text = string.Format(hindi, "{0:N}", parsed); //if you want <b>77,43,450.00</b>
return ruppesformate = text;
}
For anyone looking at this now, and getting the "No overload for method 'ToString' takes 1 argument" when using:
TotalNumber.ToString("N")
My solution has been to use :
TotalNumber.Value.ToString("N")
I often get stuck on this when working directly inside an MVC View, the following wasn't working:
#Model.Sum(x => x.Number).ToString("N")
Whereas this works:
#Model.Sum(x => x.Number).Value.ToString("N")

Remove trailing zeros and thousand separator (comma)

Since I am using this to remove trailing zeros when there is no value behind decimal:
decimal.Parse(variable).ToString("G29")
But it doesn't give thousand separator. For example:
string amount = "54321.00"
string amount2 = "54321.55"
string parsed = decimal.Parse(amount).ToString("G29");
string parsed2 = decimal.Parse(amount2).ToString("G29");
//parsed = 54321
//parsed2 = 54321.55
//my goal
//parsed = 54,321
//parsed2 = 54,321.55
Is there any better format type?
Use a custom format
string format = "#,#.##";
decimal noDecimalPlaces = 54321.00m;
decimal decimalPlaces = 54321.55m;
Console.WriteLine(noDecimalPlaces.ToString(format)); // writes 54,321
Console.WriteLine(decimalPlaces.ToString(format)); // writes 54,321.55
You can read more about formatting decimals on msdn.
The way this works
The latter part .## specifies that you allow up to two decimal places. The former part #,# specifies that you want to separate the integer part of your value.
Note:
The number formatting is still culture specific, so for cultures that use , as the decimal separator and . for digit grouping your numbers will be displayed as 54.321 and 54.321,55 instead. You can find out more about formatting in .NET here.

Conversion error parsing number formatted with a comma as the number group separator

In mvc I have formatted a numeric value using a comma as the number group separator as is specified in the Indian numbering system:
PaymentReceived = String.Format(new CultureInfo("en-IN", true), "{0:n}", t.PaymentReceived)
Later, I need to parse the string back to a numeric value. Here the FieldValue is of type long, while the right side value is of type string. How do I get the conversion to long?
I already have tried to do he following, but this is throwing an error message (the input string was not in correct format):
FieldValue = Convert.ToInt64( String.Format(new CultureInfo("en-IN", true), "{0:n}", t1.FieldValue))
As pointed out in the comment, you do not need to format a string with commas to convert it to a long. The commas are purely a presentation issue and None of the primitive number data types(int, long, double...) will maintain them.
To convert the string to long, do this.
FieldValue = Convert.ToInt64(t1.FieldValue)
To display the value as a comma separated number, do this
string formattedString = FieldValue.ToString("N0")
Your problem is not with the number group separator in your string. Your problem is that, when you formatted your number, you used the "{0:n}" format but did not specify a precision. From the docs:
If the precision specifier is omitted, the number of decimal places is defined by the current NumberFormatInfo.NumberDecimalDigits property.
For "en-IN" the default number of digits is 2. Thus your number, despite being a long, is being formatted with decimal digits. I.e. if you do:
long val = 1111111011;
var s = String.Format(new CultureInfo("en-IN", true), "{0:n0}", val);
Console.WriteLine(s);
The result looks like 1,11,11,11,011.00.
Later, you try to parse the string back to a long -- which throws a FormatException. The string is in an invalid format for a long because it contains a decimal point while long can only have integer values.
To avoid this problem, you could:
Format the initial value without a decimal point by using a precision of zero:
String.Format(new CultureInfo("en-IN", true), "{0:n0}", t.PaymentReceived)
Parse to decimal and round:
(long)Math.Round(decimal.Parse(s, new CultureInfo("en-IN", t.PaymentReceived)));

Double.TryParse() input decimal separator different than system decimal separator

I have a source XML that uses a dot (".") as a decimal separator and I am parsing this on a system that uses a comma (",") as a decimal separator.
As a result, value of 0.7 gets parsed with Double.TryParse or Double.Parse as 7000000.
What are my options to parse correctly? One of them is to replace dots in source with commas with String.Replace('.', ',') but I don't think I like this very much.
XML standard is explicit about the formatting of dates and numbers etc. This helps to ensure that the XML is platform independent and interoperable. Take a look at using XmlConvert for xml data.
double value = XmlConvert.ToDouble(stringValue);
This does the job:
string test = "0.7";
Assert.Equal(0.7, Double.Parse(test, NumberStyles.Float, CultureInfo.InvariantCulture));
double.TryParsehas an overload taking an IFormatProvider. Use a coresponding CultureInfo, in your case CultureInfo.InvariantCulture can be used.
Easy way to specify custom decimal separator:
var price = "122$00";
var nfi = new NumberFormatInfo { CurrencyDecimalSeparator = "$" };
var ok = decimal.TryParse(price, NumberStyles.Currency, nfi, out result);

Categories