Recently I’m reading the Glibc(2.16) source code on its newest release. While on the strtol, I got the following code
/* some other code ... */
while (*nptr >= '0' && *nptr <= '9') {
unsigned long int digval = *nptr - '0';
if (result > LONG_MAX / 10 ||
(sign > 0 ? result == LONG_MAX / 10 && digval > LONG_MAX % 10
: (result == ((unsigned long int) LONG_MAX + 1) / 10 &&
digval > ((unsigned long int) LONG_MAX + 1) % 10))) {
errno = ERANGE;
return sign > 0 ? LONG_MAX : LONG_MIN;
}
result *= base;
result += digval;
++nptr;
}
The code to judge the ERANGE error may seems hard to read, To make it more clearly, I recode it like that
unsigned long int
is_overflow(unsigned long int result, int sign, int digval) {
if (result > LONG_MAX / 10) {
goto error;
}
if (sign > 0) {
if (result == LONG_MAX / 10 && digval > LONG_MAX % 10)
goto error:
} else {
if (result == ((unsigned long int) LONG_MAX + 1) / 10 &&
digval > ((unsigned long int) LONG_MAX + 1) % 10)
goto error;
}
error:
errno = ERANGE;
return sign > 0 ? LONG_MAX : LONG_MIN;
}
And I write some code to test the long-if-statement:
#include <stdio.h>
#define LONG_MAX 2147483647L
int main() {
int sign = 0;
int expr_val = 0;
int digval = '2';
/* without any modification */
expr_val = LONG_MAX / 10 && digval > LONG_MAX % 10;
printf("%d\n", expr_val);
expr_val = (unsigned long int) LONG_MAX + 1) / 10 &&
digval > ((unsigned long int) LONG_MAX + 1) % 10;
printf("%d\n", expr_val);
return 0;
}
These glibc origin code seems like to judge whether result equal to true, because the expr_val alway equal to be true.
My question is why the code add such a test for result, is
result > LONG_MAX / 10
sufficient to judge the successive overflow? And no matter result == 1 or result == 0 it always to be a valid digital value, why author do this?
means
and not
And the
result > LONG_MAX / 10check alone is not enough because e.g. with yourLONG_MAXvalue, ifresultis214748364, then we also need to checkdigval, because1will not overflow, but9would.