-1

Why strtol() returns -1 for very large numbers in C?

For example:

#include <stdio.h>
#include <string.h>
int main() {
    long long i = strtoll("135898539853985649864867468746876587784760", NULL, 10);
    printf("i = %lld\n", i);
    return 0;
}
molexi
  • 530
  • 5
  • 13
  • 3
    LLONG_MAX for most platforms is signed 64 bit `9223372036854775807`. That is considerably less than what you're providing. Secondly, `strtol` returns a `long` anyway, You would want to use`strtoll`, which still doesn't fit, but at least returns the proper type. There are three lines of code in this `main`, and two of them are broken. Might need to hit the text again. – WhozCraig Jun 25 '18 at 16:40
  • 2
    You are printing a `long long` value with the format for a plain `int`. That is misleading you. It returns `LONG_MAX`. – Jonathan Leffler Jun 25 '18 at 16:41
  • A long can't be more than 63 bits, which is about 19 decimal digits. Probably, if you had bothered to capture the second argument you would see that it hasn't moved the read location. – Gem Taylor Jun 25 '18 at 16:41
  • 2
    `%d` is the wrong format specifier for `long long` leading to _undefined behavior_. Change it to `%lld`. – Ian Abbott Jun 25 '18 at 16:44
  • 2
    @gem Nothing in the C spec prevents "A long being more than 63 bits", even though it would be uncommon. – chux - Reinstate Monica Jun 25 '18 at 16:47
  • 2
    @GemTaylor The behavior in this case should be to move the read location to the end of the digit sequence, but return `LONG_MAX` and set errno to `ERANGE`. – zwol Jun 25 '18 at 16:48
  • 1
    It is always wise to ask yourself in this type of questions: What would I expect it to return? – Eugene Sh. Jun 25 '18 at 17:00
  • 2
    For `strtol` you need `stdlib.h` . – Michi Jun 25 '18 at 17:03
  • 1
    Thanks for your attention, everyone. I implemented the suggested changes. The returned value of i is still -1. Can someone answer why? – molexi Jun 26 '18 at 18:41

2 Answers2

3

Why strtol() returns -1 for very large numbers in C?

Code is mis-behaving due to using the wrong print specifier / variable combination. That is undefined behavior.

long long i = .... printf("i = %d\n", i); should raise a warning with a well warning enabled compile. Save time and enable all warnings.

Use a matching print specifier / variable: @Jonathan Leffler @Ian Abbott

long long i = ....
...
//           vvv
printf("i = %lld\n", i);

It would make more sense to use strtoll() to convert a string to a long long

int main() {
    //  long long i = strtol("1358...
    long long i = strtoll("135898539853985649864867468746876587784760", NULL, 10);
    // printf("i = %d\n", i);
    printf("i = %lld\n", i);
    return 0;
}

Printing the value of errno would indicate overflow. Testing the endptr would indicate if some conversion occurred.

As strtoll() may set errno to a non-zero value, set to 0 just prior to the function call to well assess the function's effect afterward and code did not inherit some earlier non-zero value.

    char *endptr;  
    errno = 0;
    long long i = strtoll(some_string, &endptr, 10);
    int errnum = errno;
    printf("i = %lld\n", i);
    if (some_string == endptr) {
      puts("No conversion");
    } else {
      if (errnum == ERANGE) { 
        puts("Overflow");
      } else if (errnum) {
        puts("Implementation specific error");
      } 
    }
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    "Use a matching print specifier / variable:" Credit where credit is due: @JonathanLeffler mentioned it before me (although I went into more detail). – Ian Abbott Jun 25 '18 at 17:02
  • 1
    Note that the call to `printf()` might set `errno` (it probably won't, but the standard library on Solaris used to (and probably still does) set `errno` to `ENOTTY` when the standard output was not a terminal — even though it reported no error). You should either capture the value of `errno` (e.g. `int errnum = errno;`) immediately after the call to `strtoll()` and then test `errnum` instead of `errno`, or move the printing of the value until you've finished testing `errno`. – Jonathan Leffler Jun 25 '18 at 19:25
  • @JonathanLeffler True `int errnum = errno;` is a good practice if code calls some function before evaluating or uses `errno` more than once. – chux - Reinstate Monica Jun 25 '18 at 19:31
  • I would also encourage you to mention in the text that it's necessary to set `errno` to zero immediately before calling `strtoll`, because you can't look at its return value to see if it failed, so you need to check errno unconditionally, so you need to make sure that if it's nonzero, it was `strtoll` that set it. (Your code is fine, it's just not apparent why that `errno = 0` at the top is necessary.) – zwol Jun 26 '18 at 15:53
2

Actually, strtol() returns the correct value LONG_MAX. You use the wrong format string - try printf("i = %lli\n", i);. %d is for outputting an ordinary int.

Michael Beer
  • 853
  • 5
  • 12