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

前端 未结 19 1288
孤街浪徒
孤街浪徒 2020-11-22 06:47

I want to parse a string like \"3.5\" to a double. However,

double.Parse(\"3.5\") 

yields 35 and

double.Pars         


        
相关标签:
19条回答
  • 2020-11-22 07:04

    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.

    0 讨论(0)
  • 2020-11-22 07:05
    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)));
    
    0 讨论(0)
  • 2020-11-22 07:06

    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.

    0 讨论(0)
  • 2020-11-22 07:07

    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
    
    0 讨论(0)
  • 2020-11-22 07:09

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 07:10
    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.

    0 讨论(0)
提交回复
热议问题