By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,266 Members | 1,862 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,266 IT Pros & Developers. It's quick & easy.

__atoi64 not working correctly

P: n/a
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.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #1
Share this Question
Share on Google+
17 Replies


P: n/a
Jonathan Burd wrote:
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.


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

lccwin32: http://www.cs.virginia.edu/~lcc-win32
Nov 14 '05 #2

P: n/a
jacob navia wrote:
Jonathan Burd wrote: <snip>
Your code works perfectly with lcc-win32.
I get:
18726761288: 18726761288

lccwin32: http://www.cs.virginia.edu/~lcc-win32


Intel's compiler and GCC give me this:

18726761288: 1546892104

Regards,
Jonathan.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #3

P: n/a
Jonathan Burd wrote:
jacob navia wrote:
Jonathan Burd wrote:


<snip>

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

lccwin32: http://www.cs.virginia.edu/~lcc-win32

Intel's compiler and GCC give me this:

18726761288: 1546892104

Regards,
Jonathan.


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
Nov 14 '05 #4

P: n/a
Using linux's gcc your code works OK.

Unmodified

jacob
Nov 14 '05 #5

P: n/a
On Fri, 21 Jan 2005 13:56:20 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote:
jacob navia wrote:
Jonathan Burd wrote:

<snip>

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

lccwin32: http://www.cs.virginia.edu/~lcc-win32


Intel's compiler and GCC give me this:

18726761288: 1546892104


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
Nov 14 '05 #6

P: n/a
Chris Croughton wrote:
On Fri, 21 Jan 2005 13:56:20 +0530, Jonathan Burd <snip>
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: I have ``gcc (GCC) 3.4.1 (mingw special)" running on Windows XP / P4.
printf ("%s: %lld\n", numstr, __atoi64 (numstr) / 10); This prints ``18726761288: 1872676128". Funny.
The earlier code still prints: ``18726761288: 1546892104".

lcc-win32 produces the correct output: ``18726761288: 18726761288"
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.)
The code I presented is a snippet from a larger module.
The original module has stddef.h included.
However, I missed it when posting.

Chris C


I'll try updating MinGW to the latest 3.4.2 and see what happens.

Regards,
Jonathan.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #7

P: n/a
jacob navia wrote:
<snip>
This is very clear. If you make (int)18726761288LL you
obtain 1546892104.
Interesting.
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.

<snip>

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

Regards,
Jonathan.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #8

P: n/a
On Fri, 21 Jan 2005 18:48:22 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote:
Chris Croughton wrote:
On Fri, 21 Jan 2005 13:56:20 +0530, Jonathan Burd <snip>
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:

I have ``gcc (GCC) 3.4.1 (mingw special)" running on Windows XP / P4.
printf ("%s: %lld\n", numstr, __atoi64 (numstr) / 10);

This prints ``18726761288: 1872676128". Funny.
The earlier code still prints: ``18726761288: 1546892104".


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...
lcc-win32 produces the correct output: ``18726761288: 18726761288"
You should get 1872676128 for the converted answer if your function is
working correctly.
As you do, so your function is fine it's the display of te value which
is odd.
(Incidentally, you need to #include <stddef.h> to get a definition of
NULL before your function uses it.)
The code I presented is a snippet from a larger module.
The original module has stddef.h included.
However, I missed it when posting.


No problem. Some systems seem to define NULL in all sorts of other
headers...
I'll try updating MinGW to the latest 3.4.2 and see what happens.


I hadn't realised 3.4.2 was out...

Chris C
Nov 14 '05 #9

P: n/a
Chris Croughton wrote:
On Fri, 21 Jan 2005 18:48:22 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote: <snip>
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...
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.

<snip>

I hadn't realised 3.4.2 was out...


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

Regards,
Jonathan.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #10

P: n/a
Jonathan Burd wrote:
printf ("%s: %lld\n", numstr, __atoi64 (numstr));


And printf ("%s: %I64d\n", numstr, __atoi64 (numstr));
prints exactly what I wanted to see. Weird.

Regards,
Jonathan.

--
"I'm learning to program because then I can write
programs to do my homework faster." - Andy Anfilofieff
Nov 14 '05 #11

P: n/a
Chris Croughton wrote:
.... snip ...
(Incidentally, you need to #include <stddef.h> to get a definition
of NULL before your function uses it.)


Also in stdio.h, stdlib.h, and time.h. Any one will do.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
Nov 14 '05 #12

P: n/a
Jonathan Burd wrote:

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. :)


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.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
Nov 14 '05 #13

P: n/a
On Fri, 21 Jan 2005 20:46:37 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote:
Chris Croughton wrote:
On Fri, 21 Jan 2005 18:48:22 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote:

<snip>
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...


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.


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
I hadn't realised 3.4.2 was out...


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


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

Chris C
Nov 14 '05 #14

P: n/a
On Fri, 21 Jan 2005 12:46:37 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote in comp.lang.c:
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.


<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
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #15

P: n/a
On Fri, 21 Jan 2005 11:15:32 +0000, Chris Croughton
<ch***@keristor.net> wrote in comp.lang.c:
On Fri, 21 Jan 2005 13:56:20 +0530, Jonathan Burd
<jo***********@REMOVEMEgmail.com> wrote:
jacob navia wrote:
Jonathan Burd wrote:

<snip>

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

lccwin32: http://www.cs.virginia.edu/~lcc-win32


Intel's compiler and GCC give me this:

18726761288: 1546892104


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


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
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #16

P: n/a
On Fri, 21 Jan 2005 12:34:11 +0100, jacob navia
<ja***@jacob.remcomp.fr> wrote in comp.lang.c:
Jonathan Burd wrote:
jacob navia wrote:
Jonathan Burd wrote:
<snip>

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

lccwin32: http://www.cs.virginia.edu/~lcc-win32

Intel's compiler and GCC give me this:

18726761288: 1546892104

Regards,
Jonathan.


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.


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.
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.
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.
If all else fails, download lcc-win32... It is working, at least
what this code is concerned.

jacob


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #17

P: n/a
On Fri, 21 Jan 2005 23:09:27 -0600, Jack Klein
<ja*******@spamcop.net> wrote:
<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.
Aha! I knew there must be something different...
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.
Why am I not surprised?
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.
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.
</off-topic>


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
Nov 14 '05 #18

This discussion thread is closed

Replies have been disabled for this discussion.