Tell if string to double/float/int/short/byte is out of range
Asked Answered
A

8

9

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?

Americaamerican answered 29/8, 2013 at 19:52 Comment(9)
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?Lindon
I suppose that could possibly be one solution. Maybe something more generic that would work with any container type.Americaamerican
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?Heterochromosome
You can get an idea by using Try-Catch and inspecting the exception message instead of int.TryParseSyllepsis
@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?)Lindon
@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.Lindon
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.Stop
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.Cartage
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.Gombroon
L
3

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)

Lindon answered 29/8, 2013 at 20:5 Comment(0)
S
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.

Syllepsis answered 29/8, 2013 at 20:22 Comment(0)
L
3

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)

Lindon answered 29/8, 2013 at 20:5 Comment(0)
O
3

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

string s = "2147483648";
decimal.Parse(s) > int.MaxValue;
Offensive answered 29/8, 2013 at 21:6 Comment(0)
N
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, ...

Nakada answered 29/8, 2013 at 19:59 Comment(4)
Don't use exceptions for control flow.Stonedead
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.Lindon
@ChrisSinclair But the original question wanted to know a solution for longs, doubles, etc. as well.Nakada
@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.Nakada
S
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;
}
Stench answered 29/8, 2013 at 20:5 Comment(1)
And how would you use that for doubles instead of ints, as requested by the original post?Nakada
D
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.

Decongestant answered 29/8, 2013 at 19:58 Comment(2)
Why use Exceptions for control flow when you can just use the return value of TryParse?Bicarb
TryParse returns a bool and that is not comparable to getting many different exceptions out of Parse.Decongestant
N
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.

Newark answered 29/8, 2013 at 20:2 Comment(3)
That doesn't address double/float.Cartage
Convert.ToDouble(String) and Convert.ToFloat(String) certainly do. And they are already implemented by MSDN.Newark
I was interpreting your "as the mechanism to convert things" as meaning that the code above could be use to convert doubles and floats as well. If your code is an example which partially solve the problem, with other code like that needed for the cases such as double/float, my comment above doesn't apply.Cartage
W
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.

Weinstein answered 29/8, 2013 at 19:57 Comment(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.Weinstein

© 2022 - 2024 — McMap. All rights reserved.