Reputation:
I have some lines in my code to check whether the resulting value overflows (by comparing it to it's previous iteration), and therefore if the input value is too large. This works for some values, but not for values whose increment is so large that it not only overflows, but overflows so much that the resulting value is larger than the previous iteration. For example, it triggers for 18446744073709551616
(MAX_ULONG + 1
), but not for 184467440737095516150
(MAX_ULONG * 10
). How can I address this issue? The code is as follows:
unsigned long result = 0;
unsigned long overflowCheck = 0;
int power = 0;
for (int i = (strlen(input) - 1); i >= 0; i--) {
if ((input[i] > ('0' - 1)) && (input[i] < ('9' + 1))) {
result += (input[i] - '0') * (unsigned long)pow(iBase, power++);
} else {
printf("Invalid input string.");
valid = 0;
return -1;
}
if (result < overflowCheck) {
printf("Input value too large.");
valid = 0;
return -1;
}
overflowCheck = result;
}
return result;
Upvotes: 2
Views: 421
Reputation: 144715
There are multiple problems in your code:
pow
to perform integer arithmetics: type double
may have less value bits than type unsigned long
(for example on 64-bit linux, double
has 53 value bits and unsigned long
has 64). It is simpler to multiply the current value by iBase
and add the digit value for each new digit parsed.Here is a modified version:
#include <errno.h>
#include <limits.h>
unsigned long atoul(const char *input, unsigned int iBase) {
if (iBase < 2 || iBase > 36) {
errno = EINVAL;
return 0;
}
unsigned long result = 0;
unsigned long maxval = ULONG_MAX / iBase;
int maxdigit = ULONG_MAX % iBase;
for (;;) {
int c = *input++;
int digit;
if (c >= '0' && c <= '9') {
digit = c - '0';
} else
if (c >= 'A' && c <= 'Z') {
digit = c - 'A' + 10;
} else
if (c >= 'a' && c <= 'z') {
digit = c - 'a' + 10;
} else {
break;
}
if (digit >= iBase)
break;
if (result > maxval || (result == maxval && digit > maxdigit) {
/* overflow detected */
errno = ERANGE;
return ULONG_MAX;
}
result = result * iBase + digit;
}
return result;
}
Upvotes: 2
Reputation: 172
result < 0
will always return false since result
is unsigned (and can never be less than 0. One way to check for overflow is to make sure pow()
(as a double
) is within the bounds for long
. However, the real solution here is to not use pow()
and keep everything as integers. If you work starting with the most significant digit, you can multiply result by the base (16 in this case) and add the new digit each time. This works because 1234 = base*(base*(base*(0 + 1) + 2) + 3) + 4
Some (incomplete) code would be:
int input_len = strlen(input);
for (int i = 0; i < input_len; i++) {
// After finding out which digit group input[i] is in:
result = result * iBase + (input[i] - '0');
}
Since result
will only change by a factor of 16 at most, you can check for overflow by comparing with the previous result every iteration:
unsigned long previous = result;
// Add in the next digit
if (result < previous) {
// Overflow
}
Upvotes: -1
Reputation: 223739
Suppose you want to check if x + y
overflows where x
and y
are both unsigned long
. The naive approach would be to do this:
if (ULONG_MAX < x + y)
But this will always be false because of overflow. Instead, you would do this:
if (ULONG_MAX - x < y)
This check is algebraically the same as the first attempt but avoids issues of overflow. You can do a similar check in your case:
if ((input[i] > ('0' - 1)) && (input[i] < ('9' + 1))) {
int digit = input[i] - '0';
if (ULONG_MAX / 10 < result) {
printf("overflow");
return -1;
}
result *= 10;
if (ULONG_MAX - digit < result) {
printf("overflow");
return -1;
}
result += digit;
} else {
printf("Invalid input string.");
valid = 0;
return -1;
}
Upvotes: 1