recreation of atoi function max long long error

162 Views Asked by At

I'm having an issue in the output although I defined the macro for max long long to treat overflow but it still gives me a the wrong output

# define LLONG_MAX 9223372036854775807LL

as you can see here the macro for the max long long

#include "libft.h"

static int  iswhitespace(char c)
{
    if (c == ' ' || c == '\t' || c == '\n'
        || c == '\r' || c == '\v' || c == '\f')
        return (1);
    return (0);
}

function for only white spaces

static int  ft_result(int count, long long int n, int sign)
{
    if (count > 1)
        return (0);
    else if (n > LLONG_MAX && sign == -1)
        return (0);
    else if (n > LLONG_MAX && sign == 1)
        return (-1);
    else
        return (n * sign);
}

I think the issue lays here in this function that calculates the results

int ft_atoi(const char *str)
{
    int                 i;
    unsigned long long  n;
    int                 sign;
    int                 count;

    i = 0;
    n = 0;
    sign = 1;
    count = 0;
    if (str == NULL || (str != NULL && *str == '\0'))
        return (0);
    while (iswhitespace(str[i]))
        i++;
    while (str[i] == '-' || str[i] == '+')
    {
        if (str[i] == '-')
            sign *= -1;
        count++;
        i++;
    }
    while (str[i] >= '0' && str[i] <= '9')
        n = (n * 10) + (str[i++] - '0');
    return (ft_result(count, n, sign));
}

for the main function I think the logic is solid if there is a potential segfault please point it out

#include <stdio.h>

int main()
{
    printf("my atoi: %d || original : %d",ft_atoi("9999999999999999999999999"),atoi("9999999999999999999999999"));
}

as you can see this is only the comparison between the functions output:

my atoi: 1241513983 || original : -1

2

There are 2 best solutions below

1
uncharted47 On BEST ANSWER

okay i think i fixed it thanks for the help if anyone wondering this was the solution

libft.h contains: #include <limits.h> i didint define my own macro

#include "libft.h"

static const char   *skipwhitespace(const char *c, int *sign)
{
    while (*c == ' ' || *c == '\t' || *c == '\n'
        || *c == '\r' || *c == '\v' || *c == '\f')
        c++;
    if (*c == '-' || *c == '+')
    {
        if (*c == '-')
            *sign = -1;
        c++;
    }
    return (c);
}

int ft_atoi(const char *str)
{
    int                 i;
    unsigned long long  n;
    int                 sign;

    i = 0;
    n = 0;
    sign = 1;
    if (str == NULL || (str != NULL && *str == '\0'))
        return (0);
    str = skipwhitespace(str, &sign);
    while (str[i] >= '0' && str[i] <= '9')
    {
        n = (n * 10) + (str[i++] - '0');
        if (n > LLONG_MAX && sign == 1)
            return (-1);
        if (n > LLONG_MAX && sign == -1)
            return (0);
    }
    return (n * sign);
}

i made the test case inside the calculations and use the pointer to skip whitespaces and signs


#include <stdio.h>

int main()
{
    printf("my atoi: %d || original : %d",ft_atoi("9999999999999999999999999"),atoi("9999999999999999999999999"));
}

output: my atoi: -1 || original : -1

and if anyone wonder why I don't use libraries im not allowed to use a function I didn't recreate the printf is only for testing by my side

0
chux - Reinstate Monica On

if (n > LLONG_MAX && sign == 1) makes more sense as if (n > INT_MAX && sign == 1), yet code still has at least these problems:

Consider that int and long long/unsigned long long may be the same width, so using (unsigned) long long does not portably help solve int ft_atoi(const char *str) as it does not certainly provide additional range over int.

Pedantically: INT_MAX == ULLONG_MAX may be the same.

In any case, a wider type is not needed.

Overflow not prevented

while (str[i] >= '0' && str[i] <= '9') n = (n * 10) + (str[i++] - '0'); risks overflow and undefined behavior (UB).

Instead, test if (n * 10) + (str[i++] - '0') may overflow.

while (str[i] >= '0' && str[i] <= '9') {
  int digit = str[i++] - '0';
  if (n >= LLONG_MAX/10 && 
      (n > LLONG_MAX/10 || digit > LLONG_MAX%10)) {
    ; Handle overflow with TBD code
  }
  n = n*10 + digit;
}

Accumulating the positive magnitude fails for LLONG_MIN/INT_MIN

n * sign does not well return INT_MIN as there is no n such that n * -1 results in INT_MIN is a portable well defined manner.


Alternative that does not resort to wider types:

Tested code that does not use types wider than int.

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>

int ft_atoi(const char *str) {
  const unsigned char *ustr = (const unsigned char*) str;

  // Avoid calling is...() with negative values.  Use unsigned char * access.
  while (isspace(*ustr)) {
    ustr++;
  }

  unsigned char sign = *ustr;
  if (sign == '-' || sign == '+') {
    ustr++;
  }

  int sum = 0;
  bool empty = true;
  bool overflow = false;
  while (isdigit(*ustr)) {
    empty = false;
    int digit = *ustr++ - '0';
    if (sum <= INT_MIN / 10
        && (sum < INT_MIN / 10 || digit > -(LLONG_MIN % 10))) {
      sum = INT_MIN;
      overflow = true;
      break;
    }
    // Accumulate the negative magnitude
    sum = sum * 10 - digit;
  }

  if (empty) {
    return 0;  // Maybe set errno too?
  }

  if (sign != '-') {
    if (sum < -INT_MAX) {
      sum = INT_MAX;
      overflow = true;
    } else {
      sum = -sum;
    }
  }

  if (overflow) {
    // Maybe set errno to ERANGE?
    return sum;
  }

  // Optional
  if (*ustr != '\0') {
    ; // Maybe set errno to indicate trailing non-numeric junk?
  }

  return sum;
}