My Secret Life as a Spaghetti Coder
home | about | contact | privacy statement
Shitty variable names, unnecessary usage of pointer arithmetic, and clever tricks are WTFs.

When I saw this WTF code that formats numbers to add commas, I had forgotten how low-level C really is. Even after using C++ fairly regularly for the last several months.

With 500+ upvotes, the people over at the reddit thread regarding the WTF apparently think the real WTF is the post, not the code.
nice_code, stupid_submitter - in which TheDailyWTF jumps the shark by ridiculing perfectly good code.
Let's forgive the misuse of the wornout phrase and get to whether or not looking at the code should result in utterance of WTFs.

It goes something like this:
  1. Setup, in which we use globally shared memory
  2. Negate the number if it is negative, for no reason I can think of, and set a reminder flag.
  3. Set buffer to point to the final spot. Move it back one, dereference it, and insert the null string terminator there.
  4. Move the buffer backwards. Dereference the pointer and set the value there to the character '0'. Add the remainder of the number divided by ten to that, since '1' is 1 away from '0', and so forth.
  5. Divide the number by 10 so we can get to the next digit.
  6. If we've done the loop 3 times, move the buffer pointer back one location and insert a comma.
  7. Repeat starting at step 4 until the number is 0.
  8. Cleanup - if the front character is a comma, remove it by moving the buffer pointer forward.
  9. Cleanup - Move the buffer pointer backwards and insert our '-' if the negative flag has been set.
I felt like that required too much brain power to follow for doing something so simple. So I decided to make my own version. I thought I'd try the following:
  1. Copy the number to a string.
  2. Starting at the end, copy each character to another string, inserting a comma every 3rd time.
A thousand times simpler than the convoluted mess of nice_num. Here's that attempt, in C:

char* commadify(long num, char* result) { int input_len = num_digits(num); char* input_number = malloc( input_len * sizeof(char) ); sprintf(input_number, "%ld", num); int number_of_commas = (input_len-1) / 3; int result_len = input_len + number_of_commas; int input_index = input_len-1; int result_index, count=0; for (result_index=result_len-1; result_index>=0; result_index--) { if( count == 3 ) { result[result_index] = ','; result_index --; count = 0; } result[result_index] = input_number[input_index]; input_index --; count++; } free(input_number); return result; }

I think it's clearer - but not by much. Certainly the variable names are better because you don't first have to first understand what's going on to know what they are for. I think moving the pointer arithmetic back into the more-familiar array notation helps understandability. And removing the trick of knowing that the ASCII codes for '1' is 1 more than for '0' ... for '9' is 9 more than 0 means less thinking.

Commadify is 4 times slower than nice_num

On the negative side, the commadify code is slower than nice_num, most of which is caused by using malloc instead of having preallocated memory. Removing those two instances and using preallocated memory shaves a couple of tenths of a second off of the one-million-runs loop. But you have to compensate with more code that keeps track of the start position.

So what's the verdict? I don't think we're reacting to the WTFs I mentioned above when we see the nice_num code. I think we're reacting to C itself. We're so used to very high level languages that common tricks and things you'd know as a C programmer are the WTFs to us.

This kind of stuff isn't outside the realm of what a strong programmer should know. It isn't even close to the border. The truth is our low-level skills are out of practice and we should probably get some.

What do you think?

Code with main program and comparison with nice_num is available at my github repository, miscellany/commadify.

Update: Thanks to Dave Kirby's comment, I've fixed memory leaks and updated the code in this post and at the repository. Link to repo originally was to the specific commit - so I've changed that to link to master instead.

Hey! Why don't you make your life easier and subscribe to the full post or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!


Comments
Leave a comment

There are no comments for this entry yet.

Leave a comment

Leave this field empty
Your Name
Email (not displayed, more info?)
Website

Comment:

Subcribe to this comment thread
Remember my details
Google
Web CodeOdor.com

Me
Picture of me

Topics
.NET (19)
AI/Machine Learning (14)
Answers To 100 Interview Questions (10)
Bioinformatics (2)
Business (1)
C and C++ (6)
cfrails (22)
ColdFusion (78)
Customer Relations (15)
Databases (3)
DRY (18)
DSLs (11)
Future Tech (5)
Games (5)
Groovy/Grails (8)
Hardware (1)
IDEs (9)
Java (38)
JavaScript (4)
Linux (2)
Lisp (1)
Mac OS (4)
Management (15)
MediaServerX (1)
Miscellany (76)
OOAD (37)
Productivity (11)
Programming (168)
Programming Quotables (9)
Rails (31)
Ruby (67)
Save Your Job (58)
scriptaGulous (4)
Software Development Process (23)
TDD (41)
TDDing xorblog (6)
Tools (5)
Web Development (8)
Windows (1)
With (1)
YAGNI (10)

Resources
Agile Manifesto & Principles
Principles Of OOD
ColdFusion
CFUnit
Ruby
Ruby on Rails
JUnit



RSS 2.0: Full Post | Short Blurb
Subscribe by email:

Delivered by FeedBurner