Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a string like "1.5%" and want to convert it to double value.

It can be done simple with following:

public static double FromPercentageString(this string value)
    return double.Parse(value.SubString(0, value.Length - 1)) / 100;

but I don't want to use this parsing approach.

Is any other approach with IFormatProvider or something like this?

It is culture sensitive, replace it like this:

  value = value.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, "");

Then parse it.

Used this as extenstion method for string: public static string RemovePercentageSign(this string str) { return str.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, string.Empty); } – Rob Sedgwick Jul 9, 2015 at 11:48

If you care about catching formatting errors, I would use TrimEnd rather than Replace. Replace would allow formatting errors to pass undetected.

var num = decimal.Parse( value.TrimEnd( new char[] { '%', ' ' } ) ) / 100M;

This will ensure that the value must be some decimal number followed by any number of spaces and percent signs, i.e, it must at least start with a value in the proper format. To be more precise you might want to split on '%', not removing empty entries, then make sure that there are only two results and the second is empty. The first should be the value to convert.

var pieces = value.Split( '%' );
if (pieces.Length > 2  || !string.IsNullOrEmpty(pieces[1]))
    ... some error handling ... 
var num = decimal.Parse( pieces[0] ) / 100M;

Using Replace will allow you to successfully, and wrongfully IMO, parse things like:

in addtion to 1.5%

Some cultures start with percentages, so you really need CultureInfo.CurrentCulture.NumberFormat – Harald Coppoolse Jan 6, 2016 at 13:43

Only slightly better, but less error-prone:

public static double FromPercentageString(this string value)
    return double.Parse(value.Replace("%","")) / 100;

TypeConverter provides a unified way of converting types of values to other types, as well as for accessing standard values and subproperties. http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter%28VS.80%29.aspx

This is probably overkill for one-off conversions. It is far more useful when binding properties in ASP.NET or XAML, or when parsing config files.

var result = new Percentage("1.5%");
double d = result.Value;

Percentage and its TypeConverter are defined as:

[TypeConverter(typeof(PercentageConverter))]
public struct Percentage
    public double Value;
    public Percentage( double value )
        Value = value;
    public Percentage( string value )
        var pct = (Percentage) TypeDescriptor.GetConverter(GetType()).ConvertFromString(value);
        Value = pct.Value;
    public override string ToString()
        return ToString(CultureInfo.InvariantCulture);
    public string ToString(CultureInfo Culture)
        return TypeDescriptor.GetConverter(GetType()).ConvertToString(null, Culture, this);
public class PercentageConverter : TypeConverter
    static TypeConverter conv = TypeDescriptor.GetConverter(typeof(double));
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        return conv.CanConvertFrom(context, sourceType);
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        if (destinationType == typeof(Percentage)) {
            return true;
        return conv.CanConvertTo(context, destinationType);
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        if (value == null) {
            return new Percentage();
        if (value is string) {
            string s = value as string;
            s = s.TrimEnd(' ', '\t', '\r', '\n');
            var percentage = s.EndsWith(culture.NumberFormat.PercentSymbol);
            if (percentage) {
                s = s.Substring(0, s.Length - culture.NumberFormat.PercentSymbol.Length);
            double result = (double) conv.ConvertFromString(s);
            if (percentage) {
                result /= 100;
            return new Percentage(result);
        return new Percentage( (double) conv.ConvertFrom( context, culture, value ));
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        if (!(value is Percentage)) {
            throw new ArgumentNullException("value");
        var pct = (Percentage) value;
        if (destinationType == typeof(string)) {
            return conv.ConvertTo( context, culture, pct.Value * 100, destinationType ) + culture.NumberFormat.PercentSymbol;
        return conv.ConvertTo( context, culture, pct.Value, destinationType );
                +1. IMHO it is the best answer for this question. In addition to your code, I'd also add two implicit conversion operators for a less verbose consumer code when converting to base types. One for String (static public implicit operator String(Percentage pct) { return pct.ToString(); }) and other for Decimal, since I've changed your original sample for using decimal for better precision (static public implicit operator Decimal(Percentage pct) { return pct._value; }).
– CARLOS LOTH
                Nov 1, 2010 at 21:37

It seems that many answers to this question involve replacing the culture's percentage symbol with the empty string, and then parsing the resulting string as a numeric value.

Perhaps I'm missing something, but there are still some unhandled cases here. Specifically, what happens if the PercentDecimalSeparator is different to the NumberDecimalSeparator for the current culture? What happens if the PercentGroupSeparator is different to the NumberGroupSeparator for the current culture? What happens if the PercentGroupSizes are different to the NumberGroupSizes?

Regardless of whether such a culture practically exists (if it doesn't, it may well come into existence in the future if the formatting for a culture is changed), I think that a better solution to the problem can be found if we consider these additional, special cases.

Here's a code snippet that shows a situation in which the other answers (based only on replacing the percent symbol) will fail, and a suggestion for how it could be done better properly:

        // Modify a culture so that it has different decimal separators and group separators for numbers and percentages.
        var customCulture = new CultureInfo("en-US")
                NumberFormat = { PercentDecimalSeparator = "PDS", NumberDecimalSeparator = "NDS", PercentGroupSeparator = "PGS", NumberGroupSeparator = "NGS", PercentSymbol = "PS"}
        // Set the current thread's culture to our custom culture
        Thread.CurrentThread.CurrentCulture = customCulture;
        // Create a percentage format string from a decimal value
        var percentStringCustomCulture = 123.45m.ToString("p");
        Console.WriteLine(percentStringCustomCulture); // renders "12PGS345PDS00 PS"
        // Now just replace the percent symbol only, and try to parse as a numeric value (as suggested in the other answers)
        var deceptiveNumericStringInCustomCulture = percentStringCustomCulture.Replace(customCulture.NumberFormat.PercentSymbol, string.Empty);
        // THE FOLLOWING LINE THROWS A FORMATEXCEPTION
        var decimalParsedFromDeceptiveNumericStringInCustomCulture = decimal.Parse(deceptiveNumericStringInCustomCulture); 
        // A better solution...replace the decimal separators and number group separators as well.
        var betterNumericStringInCustomCulture = deceptiveNumericStringInCustomCulture.Replace(customCulture.NumberFormat.PercentDecimalSeparator, customCulture.NumberFormat.NumberDecimalSeparator);
        // Here we mitigates issues potentially caused by group sizes by replacing the group separator by the empty string
        betterNumericStringInCustomCulture = betterNumericStringInCustomCulture.Replace(customCulture.NumberFormat.PercentGroupSeparator, string.Empty); 
        // The following parse then yields the correct result
        var decimalParsedFromBetterNumericStringInCustomCulture = decimal.Parse(betterNumericStringInCustomCulture)/100m;

Yes, the code is a bit longer, and perhaps I'm being pedantic (i.e. maybe such a culture will never actually exist). That said, it seems to me to be a more general solution. Hope it helps somebody :).

Great job! it may help me a lot. Mean time I'm surprised MS proved decimal.ToString("P2"); Why they don't provide decimal.ParseExact("P2", stringValue). – Pawel Cioch Oct 21, 2015 at 16:30 This is the best one if you want to take various cultures into account. Remark: what does "10%%" mean? According to your code it equals 0.1, but one could also say it means (10%)%, which equals 0.001. Suggestion: only remove the first percentage sign and call the function recursively. Still this does not take the PercentPositivePattern and the PercentNegativePattern into account, so if you have a number with a mix of percentage patterns from different cultures you still generate a number as if it was valid: "%-10%" – Harald Coppoolse Jan 6, 2016 at 13:50 what if Trump equates %% with decimal point with group separator tomorrow by an executive order tomorrow morning? – Boppity Bop Nov 3, 2020 at 16:37

You could also combine the top two answers to avoid accepting invalid values while keeping it flexible for different cultures.

var num = double.Parse(value.TrimEnd(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol.ToCharArray() ) ) / 100d;
// set up your format.
double doubleTest;
var numFormat = CultureInfo.CurrentCulture.NumberFormat;
NumberFormatInfo nfi = new NumberFormatInfo()
    CurrencyDecimalDigits = numFormat.PercentDecimalDigits,
    CurrencyDecimalSeparator = numFormat.PercentDecimalSeparator,
    CurrencyGroupSeparator = numFormat.PercentGroupSeparator,
    CurrencyGroupSizes = numFormat.PercentGroupSizes,
    CurrencyNegativePattern = numFormat.PercentNegativePattern,
    CurrencyPositivePattern = numFormat.PercentPositivePattern,
    CurrencySymbol = numFormat.PercentSymbol
// load it.
if (double.TryParse(value,  NumberStyles.Currency, nfi, out doubleTest))
    doubleTest /= 100D;
    // use as required.
                Great little hack, I like it. I used this in reverse to greatly simplify the code to produce percentage formatted values from my BigDecimal class since it can already produce currency values by leveraging a lot of the BCL functionality, so thanks for the idea!
– Mike Marynowski
                Mar 15, 2021 at 20:39
                The patterns need to be mapped between currencies and percentages appropriately if you are going the other way, they don't match up - have a look at the docs for each pattern property. Parsing doesn't care about the position though so the pattern value is irrelevant.
– Mike Marynowski
                Mar 16, 2021 at 4:03

Reflecting into .NET 4, here is Microsoft's implementation (found in System.Windows.Documents.ZoomPercentageConverter.ConvertBack). You can modify this to suit your needs. I alway's use MS's implementation when possible!

string str = (string) value; if ((culture != null) && !string.IsNullOrEmpty(str)) str = ((string) value).Trim(); if ((!culture.IsNeutralCulture && (str.Length > 0)) && (culture.NumberFormat != null)) switch (culture.NumberFormat.PercentPositivePattern) case 0: case 1: if ((str.Length - 1) == str.LastIndexOf(culture.NumberFormat.PercentSymbol, StringComparison.CurrentCultureIgnoreCase)) str = str.Substring(0, str.Length - 1); break; case 2: if (str.IndexOf(culture.NumberFormat.PercentSymbol, StringComparison.CurrentCultureIgnoreCase) == 0) str = str.Substring(1); break; num = Convert.ToDouble(str, culture); flag = true; catch (ArgumentOutOfRangeException) catch (ArgumentNullException) catch (FormatException) catch (OverflowException) +1 for mentioning WPF's "ZoomPercentageConverter". This will significantly help people who come to this Q&A in a WPF scenario, where they can just use the built-in converter without writing any new code at all. – Peter Duniho Jun 22, 2015 at 22:15 @C.Ross, actually it is an answer and it is the correct one, if you actually bother to read the question, you would see that he doesn't "want to use this parsing approach", when it is in fact, the only approach except perhaps Convert.ToDouble which would be equivalent. – Paul Creasey Jan 31, 2010 at 12:53 As I understand it, the OP is referring to the particular parsing approach that he had in his post. Meaning the bit between the parentheses - not the actual use of the parse method. Even if you consider your contribution an answer unfortunately I find that it is not very helpful (-1). I suggest that instead of saying "no matter what you do with it to remove the % sign" that you expand this with different options of how to deal with the % sign. This is the crux of the OP's question and also the reason I ended up on this page. This would also help others who come here looking for answers. – Ben Aug 10, 2013 at 10:25

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.