Promoting unsigned long int to long int

P

pereges

btw i want to read a string containing 3 numbers:

"1234 3333 6666"

SO the logic I used was

unsigned long l1, l2, l3;
char s[] = "1234 3333 6666";
char *endptr1, *endptr2, *endptr3;

errno = 0;

l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10);
l3 = strtoul(&endptr2, &endptr3, 10);

if (errno == ERANGE)
{
if (l1 == ULONG_MAX || l2 == ULONG_MAX || l3 == ULONG_MAX)
{
/* do the error handling */
}
}

else
{
if (s == endptr1 || endptr1 == endptr2 || endptr2 == endptr3)
{
printf("Conversion failed");
return (1);
}
}
 
B

Ben Bacarisse

pereges said:
btw i want to read a string containing 3 numbers:

"1234 3333 6666"

SO the logic I used was


unsigned long l1, l2, l3;
char s[] = "1234 3333 6666"
char *endptr1, *endptr2

errno = 0;

l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10); ^ no & needed.
l3 = strtoul(&endptr2, NULL, ), 10);
^ ditto.

Ah, no. You really need to keep the last end pointer as well and
errno can get reset, so this just does not work. The following might
work (but it's late so don't be surprised if this is not right):

bool too_big = false;
errno = 0;
l1 = strtoul(s, &endptr1, 10);
too_big |= l1 == ULONG_MAX && errno == ERANGE;
errno = 0;
l2 = strtoul(endptr1, &endptr2, 10);
too_big |= l2 == ULONG_MAX && errno == ERANGE;
errno = 0;
l3 = strtoul(endptr2, &endptr3, 10);
too_big |= l3 == ULONG_MAX && errno == ERANGE;

if (too_big)
....
else if (s == ensptr1 || endptr1 == endptr2 || endptr2 == endptr3)
...

but it might be worth writing a function to wrap strtoul that includes
the error messages that you want.
 
B

Ben Bacarisse

pereges said:
btw i want to read a string containing 3 numbers:
SO the logic I used was

unsigned long l1, l2, l3;
char s[] = "1234 3333 6666";
char *endptr1, *endptr2, *endptr3;

OK, see may other reply but ignore the comment about needing endptr3.

<snip>
 
P

pereges

pereges said:
btw i want to read a string containing 3 numbers:
"1234 3333 6666"
SO the logic I used was
unsigned long l1, l2, l3;
char s[] = "1234 3333 6666"
char *endptr1, *endptr2
errno = 0;
l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10);

^ no & needed.> l3 = strtoul(&endptr2, NULL, ), 10);

^ ditto.

Ah, no. You really need to keep the last end pointer as well and
errno can get reset, so this just does not work. The following might
work (but it's late so don't be surprised if this is not right):

bool too_big = false;
errno = 0;
l1 = strtoul(s, &endptr1, 10);
too_big |= l1 == ULONG_MAX && errno == ERANGE;
errno = 0;
l2 = strtoul(endptr1, &endptr2, 10);
too_big |= l2 == ULONG_MAX && errno == ERANGE;
errno = 0;
l3 = strtoul(endptr2, &endptr3, 10);
too_big |= l3 == ULONG_MAX && errno == ERANGE;

if (too_big)
....
else if (s == ensptr1 || endptr1 == endptr2 || endptr2 == endptr3)
...

but it might be worth writing a function to wrap strtoul that includes
the error messages that you want.

Why & is not needed ? The format of the function itself has a double
pointer to a char :

unsigned long int strtoul ( const char * str, char ** endptr, int
base );
 
P

pereges

Thanks, it runs perfectly.

Btw A |= B & C is the same as A = A || (B & C) isn't it ?
 
B

Ben Bacarisse

pereges said:
Thanks, it runs perfectly.

Btw A |= B & C is the same as A = A || (B & C) isn't it ?

No, because || is a short-circuit operator. It is the same as A = A |
(B & C).

It might be fine in this situation since once you have an input error
(in A) there may be no point in doing more, so the short-circuit
evaluation may be OK for this usage.
 
B

Barry Schwarz

Ok, I looked up the stroto* family of functions and have a question
with regards to that. What if I want to read a size_t variable from
the user and ensure that there is no overflow at the same time. eg:

size_t a;
char *ptr = NULL;
char *endptr;
char a_str[40]; /* User enters a string that we want to convert into a
size_t type variable */

puts("Enter the value of a\n");

if (fgets(a_str, 40, stdin) == NULL)
{
/* do the error handling */
}

if ((ptr = strchr(a_str, '\n') != NULL)
{
*ptr = '\0';
}

Now, I would like to change the string into a size_t type variable but
no strto* function allows it. Atmost I can convert to unsigned long
using strtoul function. If size_t is typedef as an unsigned long, then
there are no problems or else if its unsigned int it should overflow.
Is there any way out of this ? One way I can think of is to write a
seperate function for size_t and parse the string.

errno = 0;
a = strtoul(a_str, &endptr);

if (errno == ERANGE)

If SIZE_MAX < ULONG_MAX, this test is unreliable. For all values
between SIZE_MAX+1 and ULONG_MAX, errno will not be set but a will
contain the wrong value.
{
if (abs(a) == HUGE_VAL)

a is guaranteed to be non-negative so using abs() buys you nothing.
{
/* Overflow */
}
else
if (a == 0)
{
/* Underflow */

Since errno is set to ERANGE, you are guaranteed that a will not be
zero.
}
}
else
if (a == 0)
{
/* Conversion failed */

Except in the case where the user entered 0. You should be checking
endptr for conversion failures.

If you really are concerned about exceeding the range of size_t, check
you compiler's documentation or limits.h for the type with the
greatest ??_MAX value. Use the corresponding strtou?? function to
convert the input to a value of that type. Then compare the value
against SIZE_MAX.

In the (rare) case where size_t is a typedef for this "maximum" type,
you could set the units digit (endptr will tell you where it is) to
'\0', execute the strtou?? function and check against ??_MAX/10.


Remove del for email
 
B

Barry Schwarz

size_t is an unsigned integer. Unsigned integers all have the same
"MIN": 0.
You *DONT* have to check for < 0. The value cannot be less than 0.
The following types need not to be checked for < 0:

unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
size_t
uintptr_t
uintmax_t
uint8_t
uint16_t
uint32_t
uint64_t
uint_fast8_t
uint_fast16_t
uint_fast32_t
uint_fast64_t
time_t
clock_t

Maybe there are more unsigned integer types in the standard, I can not
recall.
Please, do not make another post about checking for < 0... Every time
you want to check a type for < 0 look at my post and find whether it
belongs to the list or not.

... Another Cunningham?


Ok, I'm sorry for my mistake. I forgot about it. Anyway here's my
program and there seems to be some problem :

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

int main(void)
{
size_t a; /* size_t variable */
char a_str[50]; /* String to be converted */
char *ptr = NULL;
char *endptr;
unsigned long l;

if (fgets(a_str, 50, stdin) == NULL)
{
fprintf(stderr, "Error while entering the stirng\n");
return (1);
}

if ((ptr = strchr(a_str, '\n')) != NULL)
{
*ptr = '\0';
}

errno = 0;

l = strtoul(a_str, &endptr, 0); /* Convert string to long unsigned
first */

Better to use 10 unless you really want a leading zero to mean octal.
if (errno == ERANGE)
{
if (abs(l) == HUGE_VAL)

Why do you keep taking the absolute value of a value that cannot be
negative?

HUGE_VAL is a double. It is not the value returned by strtoul when
errno is set to ERANGE. That honor belongs to ULONG_MAX.
{
fprintf(stderr, "OVERFLOW\n");
return (1);
}
else
if (l == 0)

If errno is set to ERANGE, l cannot be zero.
{
fprintf(stderr, "UNDERFLOW\n");
return (1);
}
}
else
{
if (l == 0.0)

l is an unsigned long. Why force conversion to double when 0 will do
the job just as well?
{
fprintf(stderr, "Conversion failed\n");
return (1);
}
}

if (l <= UINT_MAX) /* On my TCC, size_t is typedefed as unsigned
int */

SIZE_MAX would be better if you have it.
{
a = (size_t)l;

The cast is unnecessary (unless its **only** purpose is to silence a
warning about converting to a shorter type).
printf("\n%u", a);

For portability (you are asking people with other systems for help),
you should cast a to unsigned int.
}
else
printf("OVERFLOW");

Was it a deliberate decision to not differentiate between strtoul
overflow and size_t overflow?
return (0);
}


The program gives proper output until you enter a negative number for
which, ideally, it should print "UNDERFLOW" but it seems to print

No it should not.
"Conversion failed".

On my system it prints the value of the negative number converted to
unsigned int.


Remove del for email
 
B

Barry Schwarz

Ok, I'm sorry for my mistake. I forgot about it. Anyway here's my
program and there seems to be some problem :

snip 70 lines of previously posted code
The program gives proper output until you enter a negative number for
which, ideally, it should print "UNDERFLOW" but it seems to print
"Conversion failed".

Posting the same program repeatedly without digesting the comments
does not constitute progress.


Remove del for email
 
B

Barry Schwarz

Then how to detect unsigned integer underflow ?? or its not possible
with strtoul and it will simply report as "conversion failed"

Check the input string for a '-'.


Remove del for email
 
B

Barry Schwarz

Yeah, I noticed that -1 becomes 65535(UINT_MAX). In that case I will
modify my program to :

l = strtoul(a_str, &endptr, 0); /* Convert string to long unsigned
first */

if (errno == ERANGE)
{
if (abs(l) == HUGE_VAL)

You really do love making impossible comparisons and unnecessary
conversions.
{
fprintf(stderr, "OUT OF RANGE OF REPRESENTABLE VALUES\n");
return (1);
}
}
else
{
if (l == 0)
{
fprintf(stderr, "CONVERSION OF STRING FAILED\n");
}
}


Remove del for email
 
B

Barry Schwarz

Yes it does look odd because that would mean conversion of strings
like "0" would also fail. But this what I found on one reference site
about strtoul :

Proof positive that you should not accept internet musings as gospel.
"
On success, the function returns the converted integral number as a
long int value.

Strange, doesn't the u in the function name mean unsigned?
If no valid conversion could be performed, a zero value is returned.

This only occurs if the string is empty or the first non-white space
character is not acceptable to the number base.
If the correct value is out of the range of representable values,
ULONG_MAX is returned, an the global variable errno is set to ERANGE"

So why were you using HUGE_VAL?
Note the bit where it says "If no valid conversion could be performed
0 is returned. And yeah, instead of HUGE_VAL it should be ULONG_MAX

Note the bit that says the converted value has to be out of range. -1
converted to unsigned long is in range.


Remove del for email
 
B

Barry Schwarz

btw i want to read a string containing 3 numbers:

"1234 3333 6666"

SO the logic I used was


unsigned long l1, l2, l3;
char s[] = "1234 3333 6666"
char *endptr1, *endptr2

errno = 0;

l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10);

Didn't your compiler generate a mandatory diagnostic here. The first
argument to strtoul must be a char*. &endptr1 is a char**. The types
are incompatible.
l3 = strtoul(&endptr2, NULL, ), 10);

You don't care if strtoul found an invalid character while attempting
to convert the third number?
if (errno == ERANGE)
{
if (l1 == ULONG_MAX || l2 == ULONG_MAX || l3 == ULONG_MAX)

Do you think this can ever evaluate to false?
{
/* do the error handling */
}
}
else
{
if (s == endptr1 || endptr1 == endptr2)

There are other failure possibilities (1234 5678 9abc).
{
printf("Conversion failed");
return (1);

Use EXIT_FAILURE if you want help from people for whom 1 is not an
appropriate value to return from main.


Remove del for email
 
B

Barry Schwarz

pereges said:
btw i want to read a string containing 3 numbers:

"1234 3333 6666"

SO the logic I used was


unsigned long l1, l2, l3;
char s[] = "1234 3333 6666"
char *endptr1, *endptr2

errno = 0;

l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10); ^ no & needed.
l3 = strtoul(&endptr2, NULL, ), 10);
^ ditto.

Ah, no. You really need to keep the last end pointer as well and
errno can get reset, so this just does not work. The following might

No library function is allowed to reset errno (7.5-3).
work (but it's late so don't be surprised if this is not right):

bool too_big = false;
errno = 0;
l1 = strtoul(s, &endptr1, 10);
too_big |= l1 == ULONG_MAX && errno == ERANGE;
errno = 0;
l2 = strtoul(endptr1, &endptr2, 10);
too_big |= l2 == ULONG_MAX && errno == ERANGE;
errno = 0;
l3 = strtoul(endptr2, &endptr3, 10);
too_big |= l3 == ULONG_MAX && errno == ERANGE;

if (too_big)
....
else if (s == ensptr1 || endptr1 == endptr2 || endptr2 == endptr3)
...

but it might be worth writing a function to wrap strtoul that includes
the error messages that you want.


Remove del for email
 
S

santosh

pereges said:
No, I guess that would over complicate things. I'm only interested in
taking a input from the user and checking if it is within the range of
representable values for a particular data type. I realized scanf is
somewhat dangerous in that regard.

The strto* set of functions should be sufficient. In addition you can
always scan the input string yourself before conversion for additional
checks and restrictions. The ato* and *scanf set of function produce
undefined behaviour on overflow, and thus are a far less suitable
choice for string to numeric conversions.
 
B

Ben Bacarisse

Barry Schwarz said:
pereges said:
btw i want to read a string containing 3 numbers:

"1234 3333 6666"

SO the logic I used was


unsigned long l1, l2, l3;
char s[] = "1234 3333 6666"
char *endptr1, *endptr2

errno = 0;

l1 = strtoul(s, &endptr1, 10);
l2 = strtoul(&endptr1, &endptr2, 10); ^ no & needed.
l3 = strtoul(&endptr2, NULL, ), 10);
^ ditto.

Ah, no. You really need to keep the last end pointer as well and
errno can get reset, so this just does not work. The following might

No library function is allowed to reset errno (7.5-3).

That is not what was worrying me about the code. I did not mean
"reset to zero" -- I was worried that it would get set to some other
value that is not tested for thereby missing an error that the code
*did* want to catch. Reset was a bad word since it impiles "to zero".

In fact, strtoul never sets it to anything but one value so there is
no problem there. I suppose I could argue that one can't be sure that
a future standard will permit strtoul to set it to some other value
(or to ERANGE in some new situations) but that seems to be stretching
the point. Good catch.

Pereges: you can revert to the simpler test you had

if (errno == ERANGE && (l1 == ULONG_MAX || l2 == ... etc))

strtoul is not permitted to set errno to anything but ERANGE and it is
only permitted to do that when it returns ULONG_MAX so you can safely
test all three cases on one go.
 
P

pereges

I wrote a function for converting a string of doubles of the form
"xxxxxx yyyyyy zzzzz" into three seperate doubles. Can some one please
tell me if I done something wrong here ?

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <string.h>

int string_to_double(char *s, char **endptr, double *f)
{
int too_big = 0;

errno = 0;

*f = strtod(s, endptr);
too_big = (errno == ERANGE);

if (too_big)
{
fprintf(stderr, "OUT OF RANGE OF REPRESENTABLE VALUES
\n");
return (1);
}
else
{
if (s == *endptr)
{
fprintf(stderr, "CONVERSION OF STRING FAILED
\n");
return (1);
}
}

return (0);
}


int main(void)
{
char s[50], *ptr = NULL;
char *endptr1, *endptr2, *endptr3;
double f1, f2, f3;
int rc;

if (fgets(s, 50, stdin) == NULL)
{
fprintf(stderr, "ERROR IN INPUT STRING\n");
return (1);
}
if ((ptr = strchr(s, '\n')) != NULL)
{
*ptr = '\0';
}

rc = (string_to_double(s, &endptr1, &f1) == 0) &&
(string_to_double(endptr1, &endptr2, &f2) == 0) &&
(string_to_double(endptr2, &endptr3, &f3) == 0);


if (rc == 1)
{
printf("%f %f %f\n", f1, f2, f3);
}

return (!rc);
}
 
P

pereges

I think for unsigned long and size_t, the check for '-' or any
anything other than a digit or white space is easy. This can be
achieved by simply parsing the string before passing it on to strtoul.

while (s != '\0')
{
if (isdigit(s) || isspace(s))
{
i++;
}
else
/* do error handling */
}

With this, the problem when there are some characters in the string is
also eliminated and the function can be terminated quite early. For a
double, its a lot more difficult because there can be too many cases
and its probably not worth it but one can implement some simple things
like checking for alphabets other than 'e'.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top