Strange output when converting string to double - c#

I already searched for my problem but I wasn't successfully and that's the reason I'm here.
All I want to do is reading a string like "3.14" and convert it to double.
Enough said... here is my code:
using System;
namespace GlazerCalcApplication
{
class MainClass
{
public static void Main (string[] args)
{
string heightString;
double height;
heightString = Console.ReadLine();
height = Convert.ToDouble(heightString);
Console.WriteLine(height);
}
}
}
Output:
3.14
314
Press any key to continue...
Why is my double value not 3.14?
Instead of Convert.ToDouble() I also tried it with double.Parse() but I received the same behaviour. Reading strings like 3,14 is no problem.
Maybe I should also mention that I use MonoDevelop and a linux OS.
Thanks in advance.

Try specifying the culture as Invariant:
height = Convert.ToDouble(heightString,CultureInfo.InvariantCulture);
It seems the decimal seperator of your culture is comma instead of dot therefore dot is truncated after conversion.

Convert.ToDouble(string) uses Double.Parse(string, CultureInfo.CurrentCulture) method explicitly.
Here how it's implemented;
public static double ToDouble(String value) {
if (value == null)
return 0;
return Double.Parse(value, CultureInfo.CurrentCulture);
}
It is likely your CurrentCulture's NumberFormatInfo.NumberDecimalSeparator property is not . (dot). That's why you can't parse a string with . as a date seperator.
Example in LINQPad;
CultureInfo c = new CultureInfo("de-DE");
c.NumberFormat.NumberDecimalSeparator.Dump(); // Prints ,
As a solution, you can create a new reference of your CurrentCulture and assing it's NumberDecimalSeparator property to . like;
double height;
CultureInfo c = new CultureInfo("de-DE");
c.NumberFormat.NumberDecimalSeparator = ".";
height = Convert.ToDouble("3.14", c);

Judging by the result I take it you are in a culture zone where comma is the normal decimal separator.
Also, I take it that you want both dot and comma to be used for decimal separation.
If not, the below is not the proper solution.
The fastest solution for using both would be
height = Convert.ToDouble(heightString.Replace('.', ',');
This would mean that both dots and comma's are used as comma and thus parsed as a decimal separator.
If you only want to use a dot as separator, you can use invariantculture or a specific numberformatinfo. Invariant culture is already shown in the other posts. numberformat info example:
var nfi = new NumberFormatInfo { NumberDecimalSeparator = "." };
height = double.Parse(heightString,nfi);
For completeness, the example below shows both using numberformatinfo for setting the dot as decimal separator, as well as replacing comma with dots, so both characters are used for decimals
var nfi = new NumberFormatInfo { NumberDecimalSeparator = "." };
height = double.Parse(heightString.Replace(',', '.'),nfi);

Different .Net cultures (countries) have different decimal separators.
If you expect input values to be in some specific format - either use some particular culture or InvariantCulture. Also consider using double.Parse as it geve more flexibility on parsing the values than generic Convert.ToDouble.
var d = double.Parse(heightString, CultureInfo.InvariantCulture);
If you expect user to enter value in local format - your code is fine, but either your expectation of "local format" is wrong, or "current culture" set incorrectly.

Related

Would there be a better way of doing this?

post.Min.ToString("0.00").Replace(",", ".").Replace(".00", string.Empty)
post.Min is a double such as 12,34 or 12,00. Expected output is 12.34 or 12.
I basically want to replace the comma by a point, and cut the .00 part if any.
I am asking because I couldn't find anything, or because I don't exactly know what to search. This has an high change of being a duplicate, I simply can't find it. Please let me know.
The simplest solution would appear to be to use CultureInfo.InvariantCulture, and I reject the suggestion that this is any more complicated than using a series of replaces as you demonstrated in your question.
post.Min.ToString("0.##", CultureInfo.InvariantCulture);
# is the digit placeholder, described as the docs like this:
Replaces the "#" symbol with the corresponding digit if one is present; otherwise, no digit appears in the result string.
Try it online
If you use this in a lot of places, and that's why you want to keep it simple, you could make an extension method:
public static class MyExtensions
{
public static string ToHappyString(this double value)
{
return value.ToString("0.##", CultureInfo.InvariantCulture);
}
}
And then you just have to call .ToHappyString() wherever you use it. For example, post.Min.ToHappyString()
You can use .ToString("0.##").
like,
// Considered german culture; May be this is your current culture
CultureInfo culture = new CultureInfo("de");
double number1 = Double.Parse("12,34", culture);
double number2 = Double.Parse("12,00", culture);
Console.WriteLine(number1.ToString("0.##"));
Console.WriteLine(number2.ToString("0.##"));
Output:
12.34
12
.Net fiddle
Checkout the ToString overloads article on MSDN about examples of the N format. This is also covered in the Standard Numeric Format Strings article.
Relevant examples:
// Formatting of 1054.32179:
// N: 1,054.32
// N0: 1,054
// N1: 1,054.3
// N2: 1,054.32
// N3: 1,054.322
For the dot instead of comma to do it properly, in combination with N0 use:
System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
customCulture.NumberFormat.NumberDecimalSeparator = ".";
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;
double.ToString("0.##") to consider decimal places only if not .00 and you can create your own Number Format without using Culture:
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ".";
post.Min.ToString("0.##", nfi);

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.

Convert a string number with comma to a culture specific double number

NumberFormatInfo numberInfo = CultureInfo.CurrentCulture.NumberFormat;
double result = Convert.ToDouble("2,75", numberInfo);
result = 2.75
My current UI/culture is "de-DE".
Why don't i get 2,75 ?
Because you are not getting a string result, but a double. How you then display that double later is not influenced by your code above.
If you want to see "2,75" on your screen, you need to format the double as a string, adding numberInfo.
Try this to get 2,75:-
string.Format(System.Globalization.CultureInfo.GetCultureInfo("de-DE"), "{0:0.0}", 2.75);
or you can also try this:-
NumberFormatInfo n= new NumberFormatInfo();
n.NumberDecimalSeparator = ",";
n.NumberGroupSeparator = ".";
double d= 2.75;
string s= d.ToString(n); //2,75
You get 2.75. It's only different ways of displaying the number.
The double value contains no information about formatting. It's neither 2.75 nor 2,75, it's just a numeric value.
If you display the number using a culture that uses a comma as decimal separator, you will get what you expect, for example:
Console.WriteLine(result.ToString(CultureInfo.GetCultureInfo(1053)));
Output:
2,75

Input string was not in a correct format. calling TimeSpan.FromSeconds(Convert.ToDouble(time)) in IE on Changing Culture

Hi i m using given below method to convert in to string its working fine chrome but in IE its through exception Input string was not in a correct format. at this line
s = TimeSpan.FromSeconds(Convert.ToDouble(time));
these are values i m passing to it
600, 298.8, 65505, 69, 70, 20.5, 20.5, 20.5, 20.5, 1840.4, 682, 1040.3
in chrome its working but in IE it gives exception on second value when i change the culture to french language please help me out what is the problem
public static String ConvertTimeToString(this string time)
{
if (String.IsNullOrEmpty(time))
{
return time;
}
TimeSpan s;
if (time.IndexOf(':') >= 0)
{
s = TimeSpan.Parse(time);
}
else
{
s = TimeSpan.FromSeconds(Convert.ToDouble(time));
}
return s.ConvertTimeToString();
}
The failure is probably in the call Convert.ToDouble. Your code probably executes in a CultureInfo that has ',' as decimal separator, but your values use '.'. I would advice you to use Double.TryParse using a CultureInfo that has '.' as decimal separator instead:
Double value;
if (Double.TryParse(time, NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out value))
{
s = TimeSpan.FromSeconds(value);
}
You need to specify an IFormatProvider when you use Convert.ToDouble(time):
Convert.ToDouble(time, CultureInfo.InvariantCulture)
Specifying CulturInfo.InvariantCulture specifies a culture that expect floating points written as 1.234 (note the period). The source of your problem may be that you time is in the format 1,234 (note the comma). Or maybe it is the reverse: You don't specify an IFormatProvider and the current culture of you ASP.NET process uses comma as a decimal separator, but the string provided uses period?
If the decimal point used isn't consistent you should either fix it at the source (not sure what the source is here) or as a last resort you can replace comma by period:
Convert.ToDouble(time.Replace(",", ".") , CultureInfo.InvariantCulture)
However, trying to parse a string and not knowing the what to expect is not the best thing to do.

Format a double value like currency but without the currency sign (C#)

I feed a textbox a string value showing me a balance that need to be formatted like this:
###,###,###,##0.00
I could use the value.ToString("c"), but this would put the currency sign in front of it.
Any idea how I would manipulate the string before feeding the textbox to achieve the above formatting?
I tried this, without success:
String.Format("###,###,###,##0.00", currentBalance);
Many Thanks,
If the currency formatting gives you exactly what you want, clone a NumberFormatInfo with and set the CurrencySymbol property to "". You should check that it handles negative numbers in the way that you want as well, of course.
For example:
using System;
using System.Globalization;
class Test
{
static void Main()
{
NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
nfi = (NumberFormatInfo) nfi.Clone();
Console.WriteLine(string.Format(nfi, "{0:c}", 123.45m));
nfi.CurrencySymbol = "";
Console.WriteLine(string.Format(nfi, "{0:c}", 123.45m));
}
}
The other option is to use a custom numeric format string of course - it depends whether you really want to mirror exactly how a currency would look, just without the symbol, or control the exact positioning of digits.
string forDisplay = currentBalance.ToString("N2");
Have you tried:
currentBalance.ToString("#,##0.00");
This is the long-hand equivalent of:
currentBalance.ToString("N2");
string result=string.Format("{0:N2}", value); //For result like ### ### ##.##
You can do this with the group separator and the section separator, like this:
currentBalance.ToString("#,0.00;(#,0.00)");
This does not account for culture variances like the answer from #JonSkeet would, but this does mimic decimal place, rounding, thousands separation, and negative number handling that en-US culture currency format produces using a single custom format string.
.NET Fiddle Demo
var result = currentBalance.ToString("C").Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol, "");
CultureInfo cultureInfo = new CultureInfo("en-US");
cultureInfo.NumberFormat.CurrencySymbol = "Rs.";
Thread.CurrentThread.CurrentCulture = cultureInfo;
decimal devimalValue = 3.45M;
this.Text = devimalValue.ToString("C2"); //Rs.3.45
This may be overkill, but it rounds, formats...
#helper TwoDecimalPlaces(decimal? val)
{
decimal x = 0;
decimal y = 0;
string clas = "text-danger";
if (val.HasValue)
{
x = (decimal)val;
if (val > 0)
{
clas = "";
}
}
y = System.Math.Round(x, 2);
IFormatProvider formatProvider = new System.Globalization.CultureInfo(string.Empty);
<span class="#clas">#string.Format("{0:N2}", y)</span>
}
This simple solution works for me with US currency.
If not needing international currency support use this and replace the $ with the currency symbol(s) to be removed:
// for USD
string result = currentBalance.ToString("C").Replace("$", "")
or
// for EUR
string result = currentBalance.ToString("C").Replace("€", "")

Categories