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:
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.For simplicity, we assume that an
int
is 32 bits. Then the value ofUINT_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 anunsigned int
).Even if your input has 10 digits, it still could be greater than
UINT_MAX
. Therefore, parse the string into anunsigned long
(strtoul
) and test if the value is less than or equal toUINT_MAX
.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.