- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Bwahahah. This whole thread's topic is based from a code snippet for an embedded device, a domain where 8-bit is still king. But that's irrelevant anyway (Like an example using Pascal).
My point is simply that you can't always discount hand optimization, particularly in more niche markets where compilers may be designed around the concept that if you want performance, you *will* hand optimize and that to build such nitpicky optimizations into the compiler would not be worthwhile.
Is there any compiler out there that really optimizes *10 into a handful of shifts and a couple of adds? Does it do the same for 11, 12, 13 or do most just have a generic multiply routine?
We're in the realm of these (www.microchip.com) devices. It's a very different world from 2GHz 64bit microprocessord with built-in FPUs and two Gigs of ram hanging off the side.
Rich
Admin
Don't forget 8 bit micros either. Many of those can't do shifts by more han 1 bit at a time without a loop.
There are basically two big things wrong with that function. Using the "m" multiplier rather than starting at the most significant digit and working down so that you only need to multiply by 10 each time. That way the only 32-bit operation is addition, and the multiply always fits in one byte. That's only a performance issue though (most apps don't require high speed atoi routines). The second major problem as pointed out is the lack of correctness checking. I'm gratified to see some comments in the code.
(other lesser problems include not implementing "strtol" instead and define "atoi" from that...)
There are open source C libraries out there that can be used if necessary, even non-GPL ones. Rolling your own is seldom needed, and can be complicated to do correctly (and even harder to make it conform to the standard).
Admin
Indeed, even though modern x86s have blisteringly fast FPUs, their integer multipliers usually have a 3 or 4 cycle latency, so shifts and adds will still be quicker. Indeed, an LEA can do most of the work:
lea eax, [eax*4+eax]
add eax, eax
As for (I believe) a reasonably efficient x86 implementation of atoi (although it's limited to ASCII and bytewide characters):
_atoi:
; string is in eax, result in eax, we trash e?x
; I've not tested it, so it won't work :-)
xor ecx, ecx ; accumulate our result in here...
mov edx, ecx ; sign flag goes here
; skip over initial whitespace
dec eax ; do this so we only have to jump backwards below
ws:
movzx ebx, byte ptr [eax + 1]
or ebx, ebx
jz done ; quit at end of string
inc eax
cmp ebx, ' ' ; test for space - and for ASCII control chars too
jle ws
; sign test
cmp ebx, '+'
jne nopos
inc eax ; positive signs get ignored
jmp digit
nopos:
cmp ebx, '-'
jne gotdigit
dec edx ; edx now -1 IFF result needs negating
inc eax
; process digits - temp.result in ecx
digit:
movzx ebx, byte ptr [eax]
gotdigit:
sub ebx, '0' ; if digit is non-numeric (inc. EOS), we're done
jl done
cmp ebx, 9
ja done
lea ebx, [ecx*2 + ebx] ; D = 2*r + d...
inc eax ; help out P1 pairing a bit :-)
lea ecx, [ecx*8 + ebx] ; ...so r = 8*r + D = 10*r + d
jmp digit
done: ; out of number
lea eax, [ecx + edx] ; result = r (or r-1, if negating needed)
xor eax, edx ; (result-1) xor -1 = -result; result xor 0 = result
ret
(It's reading week, so I've got nothing better to do except my law coursework. Incidentally, why do so many computer people seem to go into law as their second career?)
Admin
Well, 'embedded' programming is my main job, and it has changed a lot in the last 5-6 years. Right now I get a well-equiped 'embedded' platform with an ARM 200mhz, 4mb ram and 8mb flash... 3y ago this still was an m86k with 1mb RAM / 2mb Flash. Go back another 2 years, and I'd get an m86k with half that memory... If you were really unfortunate you'd get a 8-bit ehm PIC-on-steroids-pretending-to-be-a-cpu (commonly used in thermostats) with 512kb or 1mb of paged battery-backupped ram (in 16kb or 32kb pages). A nightmare to port a rather big application to, and we ended up using all the available memory on a 1mb device, with only a few bytes left (page-swapping for a function call is quite code-intensive: on a m68k, the codesize was in the order of 130/140kb, on that specific device it was over 600kb...). Well - this is what you get when a salesman sells a desk-clock to do the job of the Big Ben....
The standard libraries underwent the same evolution. We provide a number of portable programs with a common hardware abstraction layer, making them available on multiple platforms, and for each of these platforms we have a specific config.h file filled with things like:
#define BUGGY_MEMCMP
#define BUGGY_MEMCPY
#define BUGGY_STRCPY
#define BUGGY_STRCMP
//#define BUGGY_PRINTF
#define BUGGY_ATOI
...
And yes, we rewrote most of those functions, using them with a capitalised first character everywhere. There are (sadly enough) platforms where very few of the lines above are commented out. The part above actually is (a partial) copy/paste from one of these platforms.
atoi() being buggy is very common, more common than you wish for at least. I can vividly imagine a certain platform actually having this exact atoi() implementation :P
A buggy printf/sprintf implementation is also very common, though basic stuff usually works fine. Especially formatting of signed numbers, leading zero and alignment format strings tend to be a problem. Currently I always use the self-written (s)printf functions to ensure consistency between platforms, since the self-written version is merely a (well-documented) stripdown. This void's some rather good compiler's (like the ARM and MRI compilers) optimizations and warnings about possibly invalid formatting strings, but it's something I'll have to live with, for the sake of portability. Should write a 'wrapper' layer though that only checks compatability with the own implementation in debug mode and in a final build let the compiler & standard libs do their job, but well - in an ideal world... sigh
That said, my implementations are well-tested now, I have well-defined test-scenario's I unleash on each new platform, and it passes them without any problems. New platforms tend to have less & less problems over the years. 5-6 years ago, this was disastrous...
I in general decided against 'platform' optimalisations in general, with a few notable exceptions. The illustration that 5 years ago, using malloc on these platforms was the coding equivalent of flying a plane into the WTC, while now this is actually no problem at all on newer platforms (and yes - I did write a portable heap-manager - though it still is a bit too buggy to be used in production). I sure hope I don't encounter a 128kb flash/32kb ram thing anymore... The stuff got so cheap it's simply not worth developping anymore for anything with less than 1mb ram/1mb flash (which is plenty for running our applications).
Admin
It was a rather small book written by P.J. Plauger (sp?) "The Standard C Library".
It was a simple book that showed AN implementation of the complete Standard C library. It was very eye openning for me. I had always thought of the C library as "magic" - to see its guts openned like that was just amazing to me (Arthur C. Clark - any advanced technology is indistinguishable from Magic). I guess that was the time almost 20 years ago that my eyes were openned and everything became "Just a matter of 1s and 0s".
Now the REAL WTF is why the author didn't grab one of MANY acceptably debugged atoi funcitons available and modify it for the platform - so at least the basic structure of the function call was correct. The real WTF is why don't people just grab an implementation that you have sitting on your machine (and if you don't have an implementation there is another WTF).
/***
*int atoi(char *nptr) - Convert string to long
*
*Purpose:
* Converts ASCII string pointed to by nptr to binary.
* Overflow is not detected. Because of this, we can just use
* atol().
*
*Entry:
* nptr = ptr to string to convert
*
*Exit:
* return int value of the string
*
*Exceptions:
* None - overflow is not detected.
*
*******************************************************************************/
int __cdecl _tstoi(
const _TCHAR *nptr
)
{
return (int)_tstol(nptr);
}
#ifndef _NO_INT64
__int64 __cdecl _tstoi64(
const _TCHAR *nptr
)
{
int c; /* current char */
__int64 total; /* current total */
int sign; /* if '-', then negative, otherwise positive */
#if defined (_MT) && !defined (_UNICODE)
pthreadlocinfo ptloci = _getptd()->ptlocinfo;
if ( ptloci != __ptlocinfo )
ptloci = __updatetlocinfo();
/* skip whitespace */
while ( __isspace_mt(ptloci, (int)(_TUCHAR)*nptr) )
#else /* defined (_MT) && !defined (_UNICODE) */
while ( _istspace((int)(_TUCHAR)*nptr) )
#endif /* defined (_MT) && !defined (_UNICODE) */
++nptr;
c = (int)(_TUCHAR)*nptr++;
sign = c; /* save sign indication */
if (c == _T('-') || c == _T('+'))
c = (int)(_TUCHAR)*nptr++; /* skip sign */
total = 0;
while ( (c = _tchartodigit(c)) != -1 ) {
total = 10 * total + c; /* accumulate digit */
c = (_TUCHAR)*nptr++; /* get next char */
}
if (sign == _T('-'))
return -total;
else
return total; /* return result, negated if necessary */
}
Captcha "Perfection"
Admin
A good point - but if the target platform has just a numeric input, surely there's a better way of converting a number from that input than calling atoi on it? (For instance, a jump table that directly adds the digit keyed to the number in question.)
Admin
Make that:
sub eax, 1 ; do this so we only have to jump backwards below
jc done ; test for NULLness :-O
ws:
movzx ebx, byte ptr [eax + 1]
Admin
While that is the wrong answer, there's nothing wrong per se with a sqrt that uses a fixed loop count. One fast sqrt algorithm uses a small table for the top few bits of the representation, then refines the lookup with one or two iterations of Newton-Rhapson. This is the method the Cell uses for sqrt with a 256- (or is it 512-?) entry table giving 12-bit accuracy and a single iteration of NR giving the full 23 bits for a float. Three iterations are appropriate for a double-precision result. Since Newton Rhapson doubles the number of bits for each iteration, a fixed count is correct assuming a fixed word size. Checking epsilons is more cycles and it's quite difficult to code correctly.
An implementation that has greater errors for larger numbers is simply failing to normalize. sqrt(4 * x) = 2 * sqrt(x) so you only need to consider the LSB of the exponent. sqrt(10000) should be as accurate as sqrt(0.611).
Admin
It's astonishing how many people in this thread can't grasp the simple point that a wrong answer obtained faster is still wrong.
Admin
"implementation-defined": An implementation can do whatever is convenient, but what it does must be documented.
"undefined": An implementation can do whatever the heck the implementer wants, and can document it or not, the standard doesn't give a whoop.
The way an implementation handles an undefined behavior scenario can never cause it to be out of compliance with the standard. Never ever. That's what "undefined" means.
Admin
Well, duh. But hardly wtf worthy. And as previously mentioned, it might NOT be premature optimisation, depending on the compiler used.
Either way its not a WTF.
Come on, you know it is a real WTF, don't you ? Start by the end of the string (as parsing by the end is surely faster on his micro-hardware for sure), add the digit to the multiplication of the subtotal by the multiplier or vice versa WTF i can not even remember the algorithm he coded, multiply the multiplier no wait let us cleverly shift it...
This guy just implemented the most complicated way for atoi, maybe to backup the false assumption that he is a genius, or to be sure to be the only one to be able to debug his code, which can be a good strategy... Or maybe he wanted to make us all laugh very loud ? I must confess that i did :o)))
As for myself this is even a FWTF as i learned programing in C and Assembler decades ago, and i can not stand how this guy is ruining the Honorable Reputation of Good Old Developers from the Good Old Days... So i suggest a new category, the f....g WTF category, and i vote for this one as FTWF of the week ;o)
And please, can the poster share some other "optimized" functions ? Thanks in advance !
Admin
So atoi could call a routine that flips a random bit in the boot sector. Cool.
Admin
Unless you have to implement your own VM. Most people never have to do that of course, just like most people never need to implement their own C run time library. That's the nature of having to port to new platforms, your "standard" support needs to be ported as well regardless of what language it is.
Admin
That doesn't always cause crashes in embedded systems. It's generally good practice to ensure that this crashes, so that you can find errors earlier during development. But a lot of people don't do the extra work or realize that it would be useful.
I remember in the past, an otherwise very bright student at my university had several glaring blind spots (at one point he said "why do we need to learn how to write compilers, we already have a C compiler.") He knew all the ins and outs of programming optimally on VAXes using C, whether i++ or ++i was faster, etc. Some of the labs started getting Sun workstations, and he complained when he ported over his code because his optimization tricks didn't work anymore. His loudest griping point was the fact that dereferencing a null pointer on a Sun caused a crash, because his code relied on the fact that a VAX would return 0 when you did that.
Admin
What annoys me about this WTF is that on the surface it could be easily avoided by adapting code from K&R or from an OSS implementation of atoi. Except:
* The K&R version -- or any other published version -- is copyrighted and I'm sure that embedding it in a product's source code is just asking for trouble.
* An "OSS" implementation of this is a legal nightmare. Taking it from a GNU implementation, for example, binds you to all of the copyleft licensing crap. So while a perfectly good implementation exists and is supposedly "free" it's not. It could throw his product into a quagmire.
So a programmer has to re-invent yet another wheel that shouldn't have been necessary. Sad.
Admin
Is NULL actually part of the standard? If I'm not mistaken (which I may very well be), NULL is simply just a define provided by many implementations, but the standard doesn't actually say anything about NULL.
Admin
but...but... its faster!
Admin
What I love about reading WTF is the WTF's that are posted as a "solution"
WHY oh WHY would atoi return -1 when it sees a character larger than '0'+9? When it is supposed to return "The equivalent value, or zero if the string does not represent a number. "
And what happens if the string contains a character less than '0'?
Admin
Perhaps the poor bugger should just have called his function something other than "atoi". It might still have all the same shortcomings, but at least it wouldn't be subject to any "but the standard says atoi must do this" complaints.
Admin
> It's astonishing how many people in this thread can't grasp the simple point that a wrong answer obtained
> faster is still wrong.
Wrong is subjective. For many embeded applications close is good enough. It is often understood that you need to jump through hoops at the embedded level. I agree that its a WTF if its on anything capable of running Linux or uCLinux... but there are loads of devices out there that dont. And they are used all the time in simple devices. AMX/DMX512 controllers and drivers, serial to serial converters etc etc.
The example of the sine table lookups is something I have done myself on any number of occasions. In many cases 6 deg accuracy is more than fine... if all you're doing is turning lights on smoothly or regulating stepper motors.
The only WTF is the ANSI stamp.
Admin
"...the right way, the wrong way, and the Max Powers way."
Admin
This is usually different on RISC processors where instructions are usually all the same size (never immediate data in a following address). On the PowerPC, loading a 16-bit immediate into a 32-bit register can done with a single instruction. Compilers tend to use the variant that doesn't set condition codes (unlike xor).
Admin
Possibly. Or I may have been misremembering this bit from the FAQ
In pre-standard code, NULL was/is sometimes defined to something unsuitable and therefore had/has to be avoided. That's less common these days.
as meaning that NULL was not (necessarily) zero.
I think it's more readable to treat them differently anyway. Not that I code much C/C++ anymore.
Rich
Admin
Unfortunately, that stuff happens. Ie, if you've got no stack space to speak of and registers are scarce (most 8-bit systems). Usually what happens is that someone writes the routine assuming re-entrancy isn't needed, but over time the application changes to make that assumption wrong. Many systems will only allow context switches at predetermined places (non-preemptive) so you're safe with non-reentrant functions as long as you make sure your interrupt handler doesn't use them.
Admin
Just a historical note for you newbies. Older manual typewriters used to have no 0 or 1 keys, you were to use O and l instead (oh and ell if you can't figure it out). Some typing texts also taught this way. I've ran across people who took that habit with them to computers and had trouble.
Admin
Yes you are quite egregiously mistaken.
Among other things the standard defines NULL as 0, although it is not necessarily zero.
Admin
Ah yes, the joys of 6502 machine code, with its slow mathematics and page boundary issues. Still, it and the 6510 provided us with some of the best computers...
Admin
The PIC compiler from Hi-Tech seems pretty sophisticated at times with what I can manage to do on the hardware. I don't think there is a GCC port. There are times when I think it's doing stupid things, but usually closer inspection clears things up, and remembering that it is often optimizing for space instead of speed. It's definately smart enough to turn "m = ((m<<2) + m) << 1)" into a function call to multiply by 5 and add the result to itself.
Admin
Newlib tends to use BSD licenses or other licenses that allow use without requiring the full application source code to be made available.
Admin
It seems like a lot of you do not appreciate how bad commercial uC compiler's optimizers are (and gcc isn't much better for the embedded systems I've used), nor do you understand how many cycles a multiply really takes. Yes, it is clearer to use '*', but this isn't always possible or advisiable. Whether it was premature or not, this function doesn't really deserve this scorn.
Is it incorrect? Yes. Is it really that bad? No.
Admin
Funny, my first reaction to this was... "Hey, at least it's commented!"
Oh, and the thing about the shift operators, it's not so weird IMO, but what is weird that you'd go so far as to define 10 as shift operations and not registerize the location...
Oh well, I guess the fact that we all see WTFs all over the place makes this a classic bit of WTF code!
Admin
There are drawbacks to XOR. It sets the condition codes, which restricts code motion. For example,
mov eax,0
cmp ebx,ecx
je Foo
In the above case, the CMP and MOV could be swapped, possibly speeding up the code. If the MOV were XOR, then CMP could not be moved, becuase the XOR clobbers the condition codes that JE depends on.
Second, the XOR creates a false dependency on eax. If a prior instruction is writing to eax, the processor has to wait for that to finish before it can execute the XOR. Except that I believe modern CPUs special-case XOR to avoid this false dependency.
Third, the drawback you cited for MOV (extra code size) is not generally an issue. Any decent x86 design should be able to sustain 4 or 6 bytes per instruction without the instruction fetch falling behind.
Bottom line: it's not so simple. Fortunately, we don't have to care. Let the compiler do its job.Someone else replied that XOR will "mark the register as unused"...?? Not sure why the processor would try to keep track of which registers are "used," or what it would do with that information. I've never heard of that.
Admin
This brings up an interesting point. At what point does similar code constitute as copyright infringement. If I had a version of atoi that compiled into identical machine code as K&R's version but looked very different, would that be ok or no? It's copyrighten but not patented.
Or what if I came up with very similar piece of code without ever having knowledge of the copyrighten version? After all, there are only so many good ways of implementing atoi, etc.
Admin
Well, knowing embedded controllers, I have to disagree on that. The Programm won't crash, as on most microcontrollers, "0" is a valid address.
(Taken the Frescale S12 as example, the address "0" is "PORTA" with default memory mapping)
It won't crash. if one want to do something like "crashing", one would either call a error-hook function or enter a while(1) loop....
Admin
This, and every other "fixed" version posted, has an overflow bug: it assumes that the range of representible positive integers is at least as big as the range of representible negative integers. This is almost never true; almost all computers nowadays use two's complement.
Try getting this atoi() to return -32768 on a machine with 16-bit signed ints.
Admin
Yes, we wouldn't want to penalize any of those performance-critical applications that use atoi in the inner loop. Far better to let an application error produce Undefined Behavior and likely lose application data. That'll teach 'em a lesson.
Wrong. Returning a value that's within the normal range of the function is one possible consequence of undefined behavior. There's nothing in ISO 9899:1999 that makes returning -1 from atoi(NULL) incorrect.
Admin
What's interesting is that this code starts at the end and works backwards. That approach doesn't make sense for atoi (requires more complexity, the strlen() operation, and the awkward "*= m" after trying to efficiently doing a "m *= 10"), but it does make sense for itoa. They must have copied code from the opposite algorithm, and done so badly...
Admin
Another EXCEPTION: Coding guidlines FORBIDS usage of standard library functions (e.g MISRA -- automotive rules)
(atoi is usally also forbidden in embedded, also dynamic memory allocation)
Admin
Stop the madness, please. This is an embedded system.
5*10 will likely be computed by:
That's at least 30 computed instructions, even more if there is no branch prediction (extra bubbles added in the pipeline during each branch).
Now compare with
No branches either. Whether or not this could have been optimised by the compiler is not known, do not speculate
Admin
ISO 9899:1999 defines NULL as an implementation-defined null pointer constant (7.17 #3), which is any integer constant expression with value 0, or such an expression cast to void * (6.3.2.3 #3). Typical implementations define NULL as "0" or "(void *)0" (without the quotes, obviously), but arbitrarily silly definitions are possible.
A null pointer constant always has value 0, even if the implementation's representation of a null pointer is not all-bits-zero. The implementation is required to generate a null pointer representation where it encounters a null pointer constant in a pointer context. I assume that's what "it is not necessarily zero" means above.
The standard also says that NULL must be defined by locale.h (7.11), stddef.h (7.17), stdio.h (7.19.1), stdlib.h (7.20), string.h (7.21.1), time.h (7.23.1), and wchar.h (7.24.1). Most of those are only required for hosted implementations, but even freestanding implementations have to provide stdlib.h, so any conforming implementation will define NULL in this way.
Admin
Next time put your contact info for resume filtering purposes. Thanks
Admin
1st one, defintly (a & 1 ) == 1
But I would write if (a & 1), only, because MCU are easier in comparing "not zero" instead to a
specific value ("==1")...
Code size is also smaller -- proved today at a current project.
My solution one was about 12 bytes smaller in code.
Admin
That would be me, and I qualified it with "for Pentium Pro class x86 CPUs". That's because of the partial register stall issue - which brings code like
mov [SOME_INT], eax
mov ax, [SOME_SHORT]
add ebx, eax ; this will stall!
screeching to a halt, because the PPro allocates separate physical registers to eax and ax in this instance and has to wait for the write to ax to retire before it can execute the add. However, Intel (cognisant of this) decreed that "xor eax, eax" would effectively insulate any use of part of eax after it from any use of all of eax before - so that
mov [SOME_INT], eax
xor eax, eax ; prevent a stall
mov ax, [SOME_SHORT]
add ebx, eax
doesn't stall. Unfortunately, as you note, "xor eax, eax" isn't recognised as not being dependent on the value of eax, so the first two instructions above have to execute in order; so if you need to break that dependency as well, you need to add the mov:
mov [SOME_INT], eax
mov eax, 0 ; break dependency on eax
xor eax, eax ; insulate against partial reg stalls
mov ax, [SOME_SHORT]
add ebx, eax
So all told, it's a lot easier, in that situation, to just write:
mov [SOME_INT], eax
movzx eax, word ptr [SOME_SHORT]
add ebx, eax
and save all the hassle :) But that's what I meant by "mark the register as unused".
Admin
achille, you say...
...but then suggest that constant multiplication will be implemented by a downcounted loop, rather than by the series of shifts and adds that machines without hardware multipliers have used for about the last 40 years....? And then suggest "m = (m<<3) + (m<<1)" as a better alternative, when m<<3 is going to end up in a loop itself on any CPU without a barrel shifter...?
...Oops.
Admin
Gah. It bugged me why I was stumped over a while( TRUE ) when nobody else was. In fact it was more of a case of while( wtf_are_people_still_using_single_character_variable_names??? )
Steve
Admin
Wow. Fascinating. It's even more complicated than I thought. Thanks for clarifying.
I guess you found this from Intel optimization guides or something? The term "partial register stall" does ring a bell. I used to skim through those kinds of books years ago.
Admin
Well, that same guy also said "do not speculate" after spending a whole page doing exactly that, so he does seem to lack consistency in his argumentation.
Achille, given how generally horrible the code in this WTF was, I'm still going with the theory that the author was a premature optimizer who probably didn't check what the compiler generated with *= 10. And if you preach going "the safe way" and writing shift/add code directly in case the compiler doesn't do it, then you are a premature optimizer, and I'm happy to be talking with you because guys with similar philosophy as yours wrote a lot of shitty code that I have to maintain at work, and I can't be as obnoxious with them as I can with a stranger on the net.
Short version: I hate you :p
But really, people seem to be quite ignorant of what compilers can achieve nowadays. You guys would probably have a heart attack if you saw the byzantine template based contraption I'm currently working on in my hobby project. Lots of classes, function calls, callbacks, temporary variables all over the place. And most of it gets elided and turned into nice, straightforward, linear code, only not excruciatingly written manually.
But noooo, compilers can't conceivably turn a multiplication into a bunch of shift and adds. Makes you wonder what those guys think compilers are actually doing using all that ram and cpu. Probably generating lots of useless, horrible, mysterious and slow code just for the heck of it.
Oh, and I do want you to have a heart attack. I used c++ and templates in a gameboy advance game, running on an arm7. And also on a nintendo ds. I may have violated some sacred law of embedded system programming there. The gods of misguided optimization are probably not amused.
Admin
On the odd chance noone else has corrected this, you fail to catch the first char of the source string when it is not a '-'.
atoi("12") == [ 2 ] =)
Admin
I have only one comment: Open source!
It would have done two things for the people developing these functions:
1. They could have copied the source, even the original source from the original stdlib implementation
2. No way in hell anyone other than the programmer responsible would ever have let that kind of implementation (and the others like it) pass the first cursory check through... i.e. it would have gotten kicked out of the version control as quickly as it was shoved into it...
;o)
/E
Admin
Oh come on - not only do you advocate ripping off Microsoft's copyrighted version of atoi() but you even quote it minus the copyright notice to encourage its reuse!
Remember to post back and let us know how it went in court...