Conrad C
Conrad C

Reputation: 746

C strtoul() bug for large values in AtmelStudio

I need to convert a large character number to a long to program my Atmega8 chip on AtmelStudio.

I tried using atol() and strtoul() and it does not work. I've read on google that there is a bug with strtoul() which only allows to use smaller values. I tried and it's true. I can convert the char "250" to a long with strtoul() but not "5555".

I've read that the fix is to make your own strtoul() function but how do I do that? I don't know the implementation of strtoul(), it's an extern in the library.

This does not work:

char *my_time = "2505";      
unsigned long new2 = (unsigned)strtoul(my_time, NULL, 10);

Upvotes: 1

Views: 372

Answers (3)

unwind
unwind

Reputation: 399949

The core of converting an (unsigned) decimal string to an integer is:

unsigned long str2int(const char *s)
{
  unsigned long x = 0;
  while(isdigit(*s))
  {
    x *= 10;
    x += *s++ - '0';
  }
  return x;
}

It's really not very complicated, it just walks through the digits, building up the value as it goes. The *s++ - '0' is perhaps a sign of me failing to rein in my "experienced C programmer's maximum terseness syndrome". Also I was in kind of a hurry. A more clear approach might be:

while(isdigit(*s))
{
  x *= 10; /* Each new digit makes the old ones worth more. */
  const int digit = *s - '0'; /* Convert digit character to small int. */
  x += digit; /* Add the current digit. */
  ++s;
}

So for instance, consider an input of "432". It would be computed as something like ((0 * 10 + 4) * 10 + 3) * 10 + 2.

Since you're working on an 8-bit microcontroller, you can probably gain a lot of code space by limiting yourself to uint16_t instead of unsigned long, if your application is limited to integers below 65,536.

Upvotes: 2

John Bollinger
John Bollinger

Reputation: 180968

It is pretty easy to convert a string of decimal digits representing a number within the range of the machine's unsigned long type to an unsigned long value. For example:

unsigned long my_atoul(const char *digits) {
    unsigned long result = 0;
    const char *c = digits;

    while ('0' <= *c && *c <= '9') {
        result = result * 10 + (*(c++) - '0');
    }

    return result;
}

If you want additional features (e.g. choice of radix) or different behavior on non-numeric or overlength input then such things can be implemented on top of that basic approach.

Upvotes: 0

zwol
zwol

Reputation: 140748

Please run this test program and tell us what it prints. If you don't have printf, figure out some way of getting us the same information that this program prints. (If you don't have errno, that part is not as important as the rest of it.)

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

int main(void)
{
    const char *my_time = "2505";
    char *endptr;
    unsigned long value;

    errno = 0;
    value = strtoul(my_time, &endptr, 10);
    printf("value=%lu endptr=%s delta=%ld errno=%d (%s)\n",
           value, endptr, (long)(endptr - my_time),
           errno, strerror(errno));
    return 0;
}

Upvotes: 0

Related Questions