Format fractional separator of C# decimal - formatting

I'm interacting with a 3rd party system that requires me to send it decimal numbers where s decimal comma must precede the fractional part. If the value has no fractional part the decimal comma must still be present.
So for example
decimal d = 123.45M; //needs to become 123,45
decimal d2 = 345M; //needs to become 345,
Is there an elegant way of formatting .net decimals for this purpose?

It seems like your 3rd party system might simply be using a different Culture. You can set the CultureInfo that you want to use and formatting will do what it needs to for that culture including dates, numbers, and currency.
Example:
CultureInfo culture = new CultureInfo("fr-FR");
Console.WriteLine(string.Format(culture, "{0}", 123.45M));
Output:
123,45
Even if you want to keep the current culture but for a given purpose override just one thing you can override them by:
culture.NumberFormat.NumberDecimalSeparator = ",";
Edit: I've found no way of doing the trailing decimal separator using only the built-in numeric formatting but you might be able to find something looking through the custom numeric format strings. Otherwise you may have to resort to simply testing for d2 % 1 == 0 and manually adding the decimal separator.
Example:
public static string FormatWithTrailing(decimal number)
{
// Change the following culture to the actual desired culture
CultureInfo culture = new CultureInfo("fr-FR");
return number % 1 == 0
? number.ToString(culture) + culture.NumberFormat.NumberDecimalSeparator
: number.ToString(culture);
}
Outputs:
123,45
345,

Related

Is format ####0.000000 different to 0.000000?

I am working on some legacy code at the moment and have come across the following:
FooString = String.Format("{0:####0.000000}", FooDouble)
My question is, is the format string here, ####0.000000 any different from simply 0.000000?
I'm trying to generalize the return type of the function that sets FooDouble and so checking to make sure I don't break existing functionality hence trying to work out what the # add to it here.
I've run a couple tests in a toy program and couldn't see how the result was any different but maybe there's something I'm missing?
From MSDN
The "#" custom format specifier serves as a digit-placeholder symbol.
If the value that is being formatted has a digit in the position where
the "#" symbol appears in the format string, that digit is copied to
the result string. Otherwise, nothing is stored in that position in
the result string.
Note that this specifier never displays a zero that
is not a significant digit, even if zero is the only digit in the
string. It will display zero only if it is a significant digit in the
number that is being displayed.
Because you use one 0 before decimal separator 0.0 - both formats should return same result.

CDec doesn't return the correct value

I make code that multiply all values of a DatagridView column. The code is working, but there's a problem. In particular, I've added this:
Return CDec(x.Cells("Quote").Value.ToString.Replace(",", "."))
That returns me the split value, for example, if I would have:
2,7 the correct value to return is: 2.7, but the code returns 27 and this is strange. I performed some trying, and if I delete the CDec like:
Return x.Cells("Quote").Value.ToString.Replace(",", ".")
The value returned is correct, but I've a contrast with this function:
Private Function MultiplyDecimals(ByVal sender As Decimal()) As Decimal
Dim Result As Decimal
If Not sender Is Nothing AndAlso Not sender.Length = 0 Then
Result = sender.Aggregate(Function(a, b) a * b)
End If
Return Result
End Function
That returns me 0 if I delete "As Decimal". So what's wrong?
UPDATE with possible solution:
Dim bles = x.Cells("Quote").Value.ToString.Replace(",", ".")
Dim key = Convert.ToDecimal(bles)
MessageBox.Show(key)
Return CDec(key)
Since it looks like you are also trying to manually convert from a different culture, you should use the .NET methods for this, rather than roll your own. You have two conversions going on: From one cultural number system to another and from string to a numeric type.
To convert a string like "2,7" which might be a French or Italian value:
Dim str As String = "2,7"
Dim decVal As Decimal
If Decimal.TryParse(str, NumberStyles.AllowDecimalPoint,
New CultureInfo("it-IT"), decVal) Then
' decVal has the value
Else
' Parse failed
End If
or:
Dim cult As New CultureInfo("fr-FR")
If Decimal.TryParse(str, NumberStyles.AllowDecimalPoint,
cult, decVal) Then ...
This can often work, but you might get an exception depending on the content:
Dim decVal As Decimal
Dim cult As New CultureInfo("fr-FR")
decVal = Convert.ToDecimal(str, cult)
Console.WriteLine(decVal.ToString)
Note: I am guessing at the culture of the data. Based on your recent, related question Google indicates "Nazione" is Italian, but data from/about Algeria might well be French Algerian.
CDec, like most of the legacy VB functions, is only equipped to work with the active culture settings. So, it will seem like it is "broken" in some cases:
Console.WriteLine(CDec("2.7")) ' --> 2.7D US/Can
Console.WriteLine(CDec("2,7")) ' --> 27D
Console.WriteLine(CDec("2,,7")) ' --> 27D
On a machine using French or Italian culture, the opposite will happen: "2.7" will come out 27, "2,7" to 2.7D, but "2,,7" will probably crash just like "2..7" would in the US/Can.
The reason for this behavior is that in US/Can, a comma looks like noise and CDec ignores it. A string with a single decimal-dot will convert, but a string with 2 is invalid. It would be the reverse for French where the comma is used: any number of dots look like noise, and only single decimal-comma format will parse.
It is all part of the reason no one included CDec in the solution: It is sub-optimal for any sort of cultural conversions. Manually removing or replacing decimal markers is even less optimal when there are methods designed to do that only when needed.
Since you have a string value, try Decimal.Parse() instead of CDec().
If that doesn't give you the result you expect, then either you're not handling the string that you think you are (in which case, try logging the exact string to a text file or similar so you can see exactly what you have), or the PC's culture settings are not what you think they are (it may be set to a culture that uses the comma, rather than a period, as the decimal separator), in which case try one of the Decimal.Parse() overloads that allows you to specify the culture, or perhaps even just remove the call to .Replace().

Culture specific coding

I need to make the application work according to system culture. But i have used many hardcoded value for Bit and byte conversion Like Dim maxlimit as double=4.999999.
When i change the cultureInfo for application the bit convertion is not proper. So I am looking for a solution How to use hard coded double values according to the system culture and to manipulate them? (like multiplication, division, addition).
I have used the below snippet to convert the value to Double according to system culture. i have set the Culture to "de-DE"
Code:
Dim l_sDoubleValue As Double
If String.Equals(CultureInfo.CurrentUICulture.ToString(), "de-DE",StringComparison.OrdinalIgnoreCase) Then
l_sDoubleValue = Convert.ToDouble(p_sValue.ToString(CultureInfo.CurrentUICulture), CultureInfo.CurrentUICulture.NumberFormat)
ElseIf String.Equals(CultureInfo.CurrentUICulture.ToString(), "en-US", StringComparison.OrdinalIgnoreCase) Then
l_sDoubleValue = Double.Parse(p_sValue)
End If
Return l_sDoubleValue
Example:
Value = 4.999999
When i convert the value to string using p_sValue.ToString(CultureInfo.CurrentUICulture)
the output is 4,999999 but when i convert the value to Double the output is 4.999999

Convert string to currency format without casting to numerical value first

This code -
Convert.ToDecimal("28.9100000000000000").ToString("c")
will convert a string containing a decimal value to a nicely formatted currency value. Is there anyway to do this without first converting the string value to a decimal?
In order to preserve culture specific currency attributes (currency symbol, separators and precision), your current approach looks like the best one.
If you know the precision and don't care about cultures, you can do some simple string manipulation:
"$" & myString.Substring(0, myString.IndexOf(".") + 3)
A simple method that I use:
MoneyString = string.format("{0:C}",DecimalValue)
How about this?
decimalString = "28.910000000000000000"
currencyString = "$" + decimalString.SubString(0, decimalString.IndexOf('.') + 3)
Of course, if you're going to do this you also need to worry about locale settings. Probably just as easy to do the numeric conversion, and let the framework do the formatting for you.

Decimal.toString() for any region using VB.NET

I have an application that deals with currency. For display purposes I use the nifty VB FormatCurrency function which will format based on the OS's region setting. So, if in France you might get 123,45 whereas in the US you would get 123.45.
To perform the calculation on these amounts I use CDec() to convert to decimal.
My problem is that when I convert the Decimal to a String using toString() it formats according to the currently set region. I need to be able to always convert the decimal into a String representation for the US, i.e. with decimal points.
I thought I would be able to do something similar to this:
.toString("#0.00")
Try:
value.ToString("C", CultureInfo.InvariantCulture)
More info here:
http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
The "C" is to format for currency, but you can use lots of different format strings. If you really want it formatted for "US" rather than the invariant culture you can do this:
value.ToString("C", CultureInfo.CreateSpecificCulture("en-US"))
This
Dim dec As Decimal = 1.25D
Dim s As String
s = dec.ToString("C2", System.Globalization.CultureInfo.CreateSpecificCulture("en-US"))
produces $1.25
This
s = dec.ToString("N2", System.Globalization.CultureInfo.CreateSpecificCulture("en-US"))
produces 1.25
Try passing InvariantCulture into your ToString() method:
Dim dec As Decimal = 1.25D
dec.ToString(System.Globalization.CultureInfo.InvariantCulture);
//writes 1.25