8

I have the following:

string outOfRange = "2147483648"; // +1 over int.MaxValue

Obviously if you have anything other than a number this will fail:

var defaultValue = 0;
int.TryParse(outOfRange, out defaultValue);

My question is, since this IS a number, and it WILL fail when you int.TryParse(), how do you tell that it failed because the string was out of the bounds of the container it's stored in?

gleng
  • 6,185
  • 4
  • 21
  • 35
  • 1
    Is parsing it to a larger capacity value first (say `long`, or `uint` if you know you only have positives) then checking against `Int32.Max/MinValue` viable? – Chris Sinclair Aug 29 '13 at 19:53
  • I suppose that could possibly be one solution. Maybe something more generic that would work with any container type. – gleng Aug 29 '13 at 19:55
  • Why don't you always use the largest capacity necessary if it is possible your value overflows smaller capacities? Can you add details on the context? – Pierre-Luc Pineault Aug 29 '13 at 19:55
  • 1
    You can get an idea by using `Try-Catch` and inspecting the exception message instead of `int.TryParse` – Yoav Aug 29 '13 at 19:56
  • @gleng: Well what's a "container type" to you? Seeing as even all the base primitives have different (but similar) parsing methods, are you really just looking for some of the typical ones (`int`, `double`, `long`?) – Chris Sinclair Aug 29 '13 at 19:56
  • @Yoav: `TryParse` won't throw an exception. (plus it's bad form to use exceptions as a control flow device) EDIT: Ahh, sorry, didn't realize you meant `Int32.Parse`. Yeah, that might work especially if you don't expect out-of-range values often at all. EDITx2: Just to clarify, if you _expect_ to have out-of-range values _often_, I think you'd be better off _avoiding_ the try/catch option electing to better handle it. – Chris Sinclair Aug 29 '13 at 19:57
  • 1
    What's the usage? It might not be necessary if it's for the sake of validation of input, considering you could just cover it in the message without having the complexity of numerous different messages. If it's for an circumstance you can't do anything about, then use the `Parse` methods instead to allow exceptions to occur, perhaps? Just a curiosity of mine & observation. – Grant Thomas Aug 29 '13 at 19:59
  • It won't work for float/double (which is a harder problem), but you could TryParse to a BigInteger, and then test that against the MinValue/MaxValue of the target type. By Yoav's solution (try catch around plain Parse) seems much cleaner and easier. – hatchet - done with SOverflow Aug 29 '13 at 20:00
  • 1
    I agree with @GrantThomas, from a user's perspective, why should they know that an `int` overflows at +- 2billion? The use cases should determine the actual range of input you're expecting, and you would write your validation depending on that range, and you would also choose the appropriate data-type. – Matthew Aug 29 '13 at 20:29

8 Answers8

4

I'd go with the Try/Catch solution for this scenario.

        string outOfRange = "2147483648";
        try
        {
            int.Parse(outOfRange);
        }
        catch (OverflowException oex)
        {

        }
        catch (Exception ex)
        { }

I know that most people here would recommend avoiding this but sometimes we just have to use it (or we don't have to but it would just save us a lot of time).
here's a little post about the efficiency of Try/Catch.

Community
  • 1
  • 1
Yoav
  • 3,326
  • 3
  • 32
  • 73
3

can parse to decimal and then check range, avoids try/catch

string s = "2147483648";
decimal.Parse(s) > int.MaxValue;
Konstantin
  • 3,254
  • 15
  • 20
2
string outOfRange = "2147483648"; // +1 over int.MaxValue
int value;
if(! int.TryParse(outOfRange, out value)) {
    try {
        int.Parse(defaultValue);
    } catch(OverflowException e) {
        // was overflow
    } catch(Exception e) {
        // was other reason
    }
}

Assuming that there are few cases where the number is too large, the overhead of exception throwing and catching may be tolerable, as the normal cases are handled with the faster TryParse method without involving exceptions.

This would work similar for other numeric data types like floats, ...

FrankPl
  • 13,205
  • 2
  • 14
  • 40
  • Don't use exceptions for control flow. – It'sNotALie. Aug 29 '13 at 20:00
  • You might instead use `Int64.TryParse` instead; if it passes you know it's out-of-range. If it fails, then you know it's bad input. – Chris Sinclair Aug 29 '13 at 20:00
  • @ChrisSinclair But the original question wanted to know a solution for longs, doubles, etc. as well. – FrankPl Aug 29 '13 at 20:02
  • @It'sNotALie. In the standard case of valid numbers, there are no exceptions. The case of overflow I would assume to be at least partly exceptional. And a solution that would first check all valid strings is not simple, especially if you want to use it for doubles as requested by the original post. Hence I would say here the use of exceptions is the exception of the rule to not use exceptions for control flow. – FrankPl Aug 29 '13 at 20:05
2

I would attempt to parse, if it fails, then attempt to parse a higher-capacity value. If the higher capacity value passes parsing, then you know it's out of range. If it fails as well, then it's bad input.

string outOfRange = "2147483648"; // +1 over int.MaxValue
int result;
if (!Int32.TryParse(outOfRange, out result))
{
    long rangeChecker;
    if (Int64.TryParse(outOfRange, out rangeChecker))
        //out of range
    else
        //bad format
}

Unfortunately, I don't think there's a way to do this generically for any type; you'd have to write an implementation for all types. So for example, what do do for Int64? Maybe use BigInteger instead:

string outOfRange = "9223372036854775808"; // +1 over Int64.MaxValue
long result;
if (!Int64.TryParse(outOfRange, out result))
{
    BigInteger rangeChecker;
    if (BigInteger.TryParse(outOfRange, out rangeChecker))
        //out of range
    else
        //bad format
}

EDIT: double floating point values may be more fun since AFAIK, there's no "BigDecimal" and you may have to also account for values that approach 0 at the very extreme (not sure about that). Possibly you could do a variation on the BigInteger check but you might also have to account for decimal points (probably a simple regex would be best here to have only numbers, an optional negative sign, and only one at most decimal point). If there are any decimal points, you'd have to truncate them out and simply check the integer portion of the string.

EDITx2: Here's a pretty ugly implementation for checking double values too:

// +bajillion over Double.MaxValue
string outOfRange = "90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.1";
double result;
if (!Double.TryParse(outOfRange, out result))
{
    string bigIntegerInput = outOfRange;

    if (!Regex.IsMatch(bigIntegerInput, @"^-?[0-9]\d*(\.\d+)?$"))
        //bad format

    int decimalIndex = bigIntegerInput.IndexOf('.');
    if (decimalIndex > -1)
        bigIntegerInput = bigIntegerInput.Substring(0, decimalIndex);

    BigInteger rangeChecker;
    if (BigInteger.TryParse(bigIntegerInput, out rangeChecker))
        //out of range
    else
        //bad format
}

But honestly, at this point I think we've just gone off the deep end. Unless you have some real performance bottleneck, or your application has out-of-range values inputted frequently, you might be better off just catching them the odd time it happens as in this answer or perhaps more simply, applying a regex to the input. In my last example, I may have as well just quit after doing the regex anyway (but I don't know off the top of my head if the TryParse implementations are more lenient, allowing for exponential/scientific notation. If so, the regex would have to cover these as well)

Community
  • 1
  • 1
Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
2

You could try parsing with BigInteger.

BigInteger bigInt;
bool isAnOutOfRangeInt = BigInteger.TryParse(input, out bigInt)
                         && (bigInt > int.MaxValue || bigInt < int.MinValue);
// if you care to have the value as an int:
if (!isAnOutOfRangeInt)
{
    int intValue = (int)bigInt;
}
Tim S.
  • 55,448
  • 7
  • 96
  • 122
1

Use the normal Parse instead of the TryParse. And then use it inside a try/catch because it will give you the appropriate exception. See this for details: http://msdn.microsoft.com/en-us/library/b3h1hf19.aspx. The exception you are looking for is OverflowException.

meilke
  • 3,280
  • 1
  • 15
  • 31
1

I would look at using System.Convert.ToInt32(String) as the mechanism to convert things; namely because OverflowException has already been implemented for you.

This is convenient because you can do something simple like

 try 
 {
      result = Convert.ToInt32(value);
      Console.WriteLine("Converted the {0} value '{1}' to the {2} value {3}.",
                    value.GetType().Name, value, result.GetType().Name, result);
 }
 catch (OverflowException) 
 {
      Console.WriteLine("{0} is outside the range of the Int32 type.", value);
 }   
 catch (FormatException) 
 {
      Console.WriteLine("The {0} value '{1}' is not in a recognizable format.",
                    value.GetType().Name, value);
 }   

and the logic is already a part of the standard System library.

Az Za
  • 394
  • 1
  • 9
0

The straight forward way would be to instead use Int32.Parse(string s) and catch OverflowException;

OverflowException
s represents a number less than MinValue or greater than MaxValue.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • 1
    @It'sNotALie. In general I'd avoid them for this kind of operation, but in this case it's not straight forward to write a replacement. If you involve `IFormatProvider`, even more so. – Joachim Isaksson Aug 29 '13 at 20:08