Number with decimal separator incorrectly cast to Double
Asked Answered
S

2

7

I'm experiencing weird behavior when converting a number to a double, when using culture information.
When converting "3,3" using the Dutch culture, is handled correctly. If I convert "3,3" using the US culture, it returns 33. I was expecting an error. See my example:

static void Main(string[] args)
{
    CultureInfo cultureDutch = new CultureInfo("nl-NL");
    CultureInfo cultureUS = new CultureInfo("en-US");

    System.Threading.Thread.CurrentThread.CurrentCulture = cultureDutch;
    Console.WriteLine("Input 3,3 --> Expected 3,3");
    Console.WriteLine("Output = " + Convert.ToDouble("3,3", cultureDutch));
    // Actual result --> 3,3

    Console.WriteLine("Input 3,3 --> Expected InvalidCastException");
    Console.WriteLine("Output = " + Convert.ToDouble("3,3", cultureUS));
    // Actual result --> 33

    Console.WriteLine();
    Console.WriteLine();

    System.Threading.Thread.CurrentThread.CurrentCulture = cultureUS;
    Console.WriteLine("Input 3.3 --> Expected InvalidCastException");
    Console.WriteLine("Output = " + Convert.ToDouble("3.3", cultureDutch));
    // Actual result --> 33

    Console.WriteLine("Input 3.3 --> Expected 3.3");
    Console.WriteLine("Output = " + Convert.ToDouble("3.3", cultureUS));
    // Actual result --> 3.3
    Console.ReadLine();
}

What is the correct way to handle this? I would prefer an exception when a decimal (or thousand) separator is invalid.

Spar answered 27/1, 2016 at 9:50 Comment(1)
The comma is the thousands separator, .NET is not picky about where the user puts it. Or that a user has an irregular pattern, like Indians do. You need to pursue the real fix and know what kind of language the user speaks. That is never a real problem, it is Thread.CurrentCulture. You only have this problem when you speak Dutch and pretend to be not from Holland without telling your OS about it.Bumf
S
0

Together with the answer from Boas Enkler, I've been able to solve the problem. So first I'll scan the input for thousand separators, based on current culture. And finally I'll parse the input value to a double.

private static string RemoveThousandSeparator(string input)
    {
        Regex removeThousandSeparatorExpr = new Regex(@"^-?(?:\d+|\d{1,3}(?:\"
                        + <CultureInfo>.NumberGroupSeparator + @"\d{3})+)(?:\"
                        + <CultureInfo>.NumberDecimalSeparator + @"\d+)?$");
        Match match = removeThousandSeparatorExpr.Match(input);
        if (match.Success)
        {
            input = input.Replace(<CultureInfo>.NumberGroupSeparator, "");
        }
        else
        {
            throw new Exception("Invalid input value");
        }

        return input;
    }
Spar answered 27/1, 2016 at 15:57 Comment(0)
S
2

If you just want to parse it I would use the dedicated parse methods in which you can set Numberstyles

The following code would throw a FormatException

var culture =new CultureInfo("en-US");
var result = double.Parse("3,3", NumberStyles.AllowDecimalPoint, culture);

For further information see Double.Parse Method

Setzer answered 27/1, 2016 at 9:54 Comment(1)
I thought this was the answer, but unfortunately it is not. Since the input can contain both decimal and thousand separators disallowing thousand separators is not the answer. In the US culture 1,000.0 is correct, but using the AllowDecimalPoint, it is considered invalid. I think I need a regex to strip the thousand separators, prior to using the values in calculations.Spar
S
0

Together with the answer from Boas Enkler, I've been able to solve the problem. So first I'll scan the input for thousand separators, based on current culture. And finally I'll parse the input value to a double.

private static string RemoveThousandSeparator(string input)
    {
        Regex removeThousandSeparatorExpr = new Regex(@"^-?(?:\d+|\d{1,3}(?:\"
                        + <CultureInfo>.NumberGroupSeparator + @"\d{3})+)(?:\"
                        + <CultureInfo>.NumberDecimalSeparator + @"\d+)?$");
        Match match = removeThousandSeparatorExpr.Match(input);
        if (match.Success)
        {
            input = input.Replace(<CultureInfo>.NumberGroupSeparator, "");
        }
        else
        {
            throw new Exception("Invalid input value");
        }

        return input;
    }
Spar answered 27/1, 2016 at 15:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.