__atoi64 not working correctly

Discussion in 'C Programming' started by Jonathan Burd, Jan 21, 2005.

  1. Greetings everyone,

    Reading about the int64_t types added by C'99, I decided to implement
    a 64-bit version of atoi for my own library. However, for
    the test number provided, it isn't doing what it is supposed to do.
    (I used GCC/MingW32 as the compiler with C'99 enabled. Oh, and there
    is no overflow-checking in this code!) The code follows:

    #include <ctype.h>

    #ifdef __C99__
    #include <stdint.h>

    int64_t
    __atoi64 (const char *numstr)
    {
    int64_t result = 0; /* stores the resulting number */
    register int ch; /* current character */
    int sign = '+'; /* sign of the number */

    /* Try removing this and pass (const char*)NULL. ;) */
    if (NULL == numstr)
    return 0;

    /* skip leading whitespace */
    while (isspace(*numstr))
    ++numstr;

    /* get the sign. incl. '+' because it is valid! */
    sign = *numstr;
    if ('+' == sign || '-' == sign)
    ++numstr;

    /* shift digits to left by one place each time
    * and stick the new digit in.
    */
    while ( (ch = *numstr) && isdigit(ch) )
    {
    result *= 10;
    result += ch - '0';
    ++numstr;
    }

    /* return with sign. */
    return (('-' == sign) ? -result : result);
    }
    #endif /* not __C99__ */

    #ifdef TEST
    #include <stdio.h>
    int64_t __atoi64 (const char *numstr);

    int
    main (int argc, char **argv)
    {
    const char *numstr = "18726761288";

    /* Implementation-specific:
    * LONG_LONG_MAX as in limits.h = 9223372036854775807LL
    * typedef long long int64_t;
    */

    #ifdef __C99__
    printf ("%s: %lld\n", numstr, __atoi64 (numstr));
    #endif /* not __C99__ */

    return 0;
    }
    #endif /* not TEST */

    I wonder why this isn't working. Any help will be appreciated.
    Nap time. :)

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #1
    1. Advertisements

  2. Jonathan Burd

    jacob navia Guest

    Your code works perfectly with lcc-win32.
    I get:
    18726761288: 18726761288

    lccwin32: http://www.cs.virginia.edu/~lcc-win32
     
    jacob navia, Jan 21, 2005
    #2
    1. Advertisements

  3. Intel's compiler and GCC give me this:

    18726761288: 1546892104

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #3
  4. Strange, gcc 3.0.4 (GNU/Linux / Duron 1200) and gcc 3.3.3 (Cygwin /
    WinXP / P4) both give 18726761288: 18726761288. Could it be the library
    not implementing the %lld correctly? What happens if you put the printf
    as:

    printf ("%s: %lld\n", numstr, __atoi64 (numstr) / 10);

    You should get 1872676128 for the converted answer if your function is
    working correctly.

    (Incidentally, you need to #include <stddef.h> to get a definition of
    NULL before your function uses it.)

    Chris C
     
    Chris Croughton, Jan 21, 2005
    #4
  5. Jonathan Burd

    jacob navia Guest

    This is very clear. If you make (int)18726761288LL you
    obtain 1546892104.

    Your code seems correct to me. The compilers or their
    implementation of printf are buggy.
    Note that the Intel compiler needs some extra switch to
    get into "C99" mode. See the documentation.

    If you are using gcc under windows it doesn't seem to work
    with some C99 features.

    If all else fails, download lcc-win32... It is working, at least
    what this code is concerned.

    jacob
     
    jacob navia, Jan 21, 2005
    #5
  6. Jonathan Burd

    jacob navia Guest

    Using linux's gcc your code works OK.

    Unmodified

    jacob
     
    jacob navia, Jan 21, 2005
    #6
  7. I have ``gcc (GCC) 3.4.1 (mingw special)" running on Windows XP / P4.
    This prints ``18726761288: 1872676128". Funny.
    The earlier code still prints: ``18726761288: 1546892104".

    lcc-win32 produces the correct output: ``18726761288: 18726761288"
    The code I presented is a snippet from a larger module.
    The original module has stddef.h included.
    However, I missed it when posting.
    I'll try updating MinGW to the latest 3.4.2 and see what happens.

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #7
  8. jacob navia wrote:
    <snip>

    Yes, the switch is -Qc99 and I do have it enabled.
    By the way, lcc-win32 gives the expected result.

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #8
  9. That implies that somewhere, possibly in the library implementation of
    printf, it is being truncated to a long (1872676128 will fit in a 32 bit
    long, 1546892104 is 1872676128 truncated to 32 bits).

    You are absolutely, positively certain that you are using &lld and not
    &ld? And your function is actually being declared as returning long
    long? No insult intended, but I've done sillier things myself and they
    are surprisingly hard to spot...
    As you do, so your function is fine it's the display of te value which
    is odd.
    No problem. Some systems seem to define NULL in all sorts of other
    headers...
    I hadn't realised 3.4.2 was out...

    Chris C
     
    Chris Croughton, Jan 21, 2005
    #9
  10. Yes, I checked. And then, I rechecked just to make sure my eyes are
    alright. I got a friend to see it too. He says he saw %lld and
    "long long." j/k Heh.

    Yes, it is out. A release candidate of the setup is available as of now.

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #10
  11. And printf ("%s: %I64d\n", numstr, __atoi64 (numstr));
    prints exactly what I wanted to see. Weird.

    Regards,
    Jonathan.
     
    Jonathan Burd, Jan 21, 2005
    #11
  12. Jonathan Burd

    CBFalconer Guest

    Also in stdio.h, stdlib.h, and time.h. Any one will do.
     
    CBFalconer, Jan 21, 2005
    #12
  13. Jonathan Burd

    CBFalconer Guest

    I get:

    [1] c:\c\junk>gcc -std=c99 -D TEST junk.c
    junk.c:50: parse error before "__atoi64"
    junk.c:50: warning: type defaults to `int' in declaration of
    `__atoi64'
    junk.c:50: warning: data definition has no type or storage class

    Remember, providing int64_t is optional.
     
    CBFalconer, Jan 21, 2005
    #13
  14. If I had a pound for every time I have rechecked my own code and not
    spotted the obvious error <g>...

    Library function error seems all that's left. Oh, you could try with
    printf("%llX\n", 0x12345678901LL); and that sort of thing to see whether
    that works. Also try something like printf("%lld %d\n", 12345LL, 999);
    to see whether printf() is actually taking the long long argument and
    converting it to a long or if it's taking the argument as a long in
    which case the second field will be zero:

    #include <stdio.h>
    int main(void)
    {
    printf("%llX\n", 0x12345678901LL);
    printf("%lld %d\n", 12345LL, 999);
    printf("%ld %d\n", 12345LL, 999);
    return 0;
    }

    12345678901
    12345 999
    12345 0
    3.4.1 is available for Cygwin, but for some reason my upgrade didn't
    seem to work...

    Chris C
     
    Chris Croughton, Jan 21, 2005
    #14
  15. Jonathan Burd

    Jack Klein Guest

    <off-topic>

    mingw is a "typical" gcc distribution, as is available for many
    platforms. It provides a tools like compiler and linker, and headers,
    but no library. It uses the platform's standard run-time library, in
    this case Microsoft's MSVC something or other DLL.

    The *printf() implementation in Microsoft's C library merely ignores
    the first 'l' in an "%lld" or other "%ll" conversion specifier, and
    treats the argument as a long, not a 32-bit value.

    You can verify this, if you have Visual C++, by building and running
    the following program:

    #include <stdio.h>


    int main(void)
    {
    __int64 i64 = 187267612;
    i64 *= 100;
    i64 += 88;
    printf("%lld\n", i64);
    return 0;
    }

    The output will be the same that you received.

    So if you want real long long support under Windows, you need an
    implementation that provides its own library and supports it. These
    include lcc-win32, Pelles C, and probably the gcc implementation that
    runs under Cygwin, but I don't know this last one for sure.

    </off-topic>
     
    Jack Klein, Jan 22, 2005
    #15
  16. Jonathan Burd

    Jack Klein Guest

    1. mingw and Intel's compiler use Microsoft's C library, and its
    printf() implementation discards one of the 'l' characters in a
    conversion specifier that starts with "%ll", perhaps in the assumption
    that the programmer made a typographical error. In any case, trying
    to convert a long long using a C90 library, and that's what it is, is
    undefined.

    2. Presumably he included <stdio.h> before calling printf(), and that
    defines NULL.
     
    Jack Klein, Jan 22, 2005
    #16
  17. Jonathan Burd

    Jack Klein Guest

    Not buggy, mingw uses Microsoft's C library, the one provided in a DLL
    and used by Windows system code. And it's not buggy, it's just a C90,
    not a C99, library.
    I haven't used it in a while, but I seem to remember gcc running under
    Cygwin works correctly. Again, the cygwin/gcc combination provides
    its own libraries, instead of using Microsoft's.
     
    Jack Klein, Jan 22, 2005
    #17
  18. Aha! I knew there must be something different...
    Why am I not surprised?
    I can say for sure (and did in an earlier message) -- it works fine. It
    still isn't 100% compliant, though, as I recall it doesn't support %a or
    the v (maxint) length modifier for integers. And some of the new macros
    are missing from header files.
    As someone else said recently, this means that in practice what real C
    programmers have to work to is the C89/90 standard, we can be pretty
    sure in most cases that compilers will support that (there are a few
    unfortunate programmers who still have to support things without
    function prototypes). The C99 standard is largely irrelevant from the
    point of view of writing portable code, and will be until the majority
    of the C compilers available are C99 compliant (modulo bugs, of course).

    For instance, I want to determine the 'best' floating point type for my
    application (enough significant digits with the least storage to do
    that) in the
    preprocessor. Simple, right? Something like

    #include <float.h>
    #if FLT_MANT_DIG >= 10
    typedef float my_float;
    #if DBL_MANT_DIG >= 10
    typedef double my_float;
    #if LDBL_MANT_DIG >= 10
    typedef long double my_float;
    #else
    # error No useful floating type!
    #endif

    Wrong! It's fine for C99, where the values are required to be constant
    integer expressions, but C89 says:

    Of the values in the <float.h> header, FLT_RADIX shall be a constant
    expression suitable for use in #if preprocessing directives; all
    other values need not be constant expressions. All except FLT_RADIX
    and FLT_ROUNDS have separate names for all three floating-point
    types. The floating-point model representation is provided for all
    values except FLT_ROUNDS .

    (para 2.2.4.1). So the only way to test them is to have a program which
    includes float.h and then prints out a header file containing
    appropriate definitions (for that and other reasons I have such a
    program which calculates a load of stuff about the environment -- like
    the number of bits in integer types -- and outputs a header file with
    the information because things like sizeof aren't allowed in
    preprocessor tests).

    Chris C
     
    Chris Croughton, Jan 22, 2005
    #18
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.