Home > Net >  Determining the range of an unsigned int
Determining the range of an unsigned int

Time:11-13

An assignment is requesting that I qualify input number to ensure that it is within range of an unsigned-int on my machine. How do I determine what that range is? it says that the input needs to be taken in at execution time and that no assumptions can be made about what was entered.

I tried using plugging in different ranges (2^7 , 2^15 , 2^31) but they all resulted in overflow.

CodePudding user response:

If you are using the function strtoul for converting the string input to an integer, then you don't need to determine yourself whether the input is in the range 0 to ULONG_MAX. The function strtoul will report this, by setting the value of errno accordingly. However, depending on the platform, an unsigned long may be able to represent more numbers than an unsigned int. Therefore, after the function strtoul reports that the input range is ok, you should additionally verify that the number is smaller than UINT_MAX.

Here is an example program which uses a function get_unsigned_int_from_user which will continue prompting the user for input, until the input is valid and in the range of an unsigned int:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

unsigned int get_unsigned_int_from_user( const char *prompt )
{
    for (;;) //loop forever until user enters a valid number
    {
        char buffer[1024], *p;
        unsigned long ul;

        //prompt user for input
        fputs( prompt, stdout );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        ul = strtoul( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        //make sure that number is representable as an "unsigned int"
        if ( errno == ERANGE || ul > UINT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfh4q" gets rejected
        for ( ; *p != '\0'; p   )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return ul;

    continue_outer_loop:
        continue;
    }
}

int main( void )
{
    unsigned int num;

    num = get_unsigned_int_from_user( "Please enter a number: " );

    printf( "Input is valid, you entered: %u\n", num );
}

This program has the following behavior:

Please enter a number: 5000000000
number out of range error
Please enter a number: 4000000000
Input is valid, you entered: 4000000000

The program behaves this way because UINT_MAX has the value 4294967295 on most common platforms.

The function get_unsigned_int_from_user is a slightly modified version of my function get_int_from_user from this answer of mine. See that answer for further information on how that function works.

CodePudding user response:

  1. Each implementation of the C Standard made choices about the size of fundamental (here integer) types. So an int is guaranteed to be at least 16 bits wide, but it is possible to be 32 or 64 bits wide. See: Integer types and Data models.

  2. For simplicity, we assume that an int is 32 bits. Then the value of UINT_MAX would be: 4294967295. This token is 10 digits long. So your input is definitively out of range if it is longer than 10 digits (@chux-ReinstateMonica made a good point in the comments below, e.g. an input of " 000000000000000001" would be longer than 10 digits but still in the range of an unsigned int).

  3. Even if your input has 10 digits, it still could be greater than UINT_MAX. Therefore, parse the string into an unsigned long (strtoul) and test if the value is less than or equal to UINT_MAX.

  4. If for any reason strtoul shouldn't be available to you, i recommend to use the implementation provided by Andreas Wenzel.

CodePudding user response:

To test if an string converts to a unsigned

Use strtoul() to convert to a unsigned long. Yet since that function rolls over negative text to positive integers, use strtol() first.

bool valid_unsigned(const char *s, unsigned *uval) {
  char *endptr;
  errno = 0;
  int base = 0; // Use 10 here if only decimal text acceptable.
  long lvalue = strtol(s, &endptr, base);
  if (s == endptr) {
    // No conversion
    *uval = 0;
    return false;
  }
  if (lvalue < 0) {
    // Negative
    errno = ERANGE;  // Perhaps calling code would like to test this.
    *uval = 0;
    return false;
  }
  if ((unsigned long) lvalue <= UINT_MAX && errno == 0) {
    // Success
    *uval = (unsigned) lvalue;
    return true;
  }

  #if UINT_MAX > LONG_MAX
    // Still could be a value in the LONG_MAX...UINT_MAX range.
    errno = 0;
    unsigned long uvalue = strtoul(s, &endptr, base);
    if (uvalue > UINT_MAX || errno) {
      // Too big
      *uval = UINT_MAX;
      return false;
    }
    *uval = (unsigned) uvalue;
    return true;
  #else
    *uval = UINT_MAX;
    return false;
  #endif 
} 

To do: Test trailing text.

  •  Tags:  
  • c
  • Related