K&R C Exercise 3-4: itoa — Handle INT_MIN in Integer to String

Exercise 3-4. In a two’s complement number representation, our version of itoa does not handle the largest negative number, that is, the value of n equal to −(2wordsize−1). Explain why not. Modify it to print that value correctly, regardless of the machine on which it runs.

This exercise has two parts: explain the bug, then fix it.

Why the Original Fails

The K&R itoa negates n when it is negative (n = -n), then works with the positive value. On a 32-bit system, INT_MIN = -2147483648. Its mathematical negation is +2147483648, but INT_MAX = 2147483647 — the positive range is one smaller. Attempting -INT_MIN is signed integer overflow, which is undefined behaviour in C. In practice on most hardware, the result wraps back to the same bit pattern, producing -2147483648 again — and itoa loops forever or produces garbage.

The Fix: Work in the Negative Domain

Instead of flipping the sign, stay negative throughout. Extract digits as -(n % 10) while n remains negative. Since n % 10 is non-positive when n is negative, negating it gives a digit in 0–9. The loop condition becomes (n /= 10) < 0 (keep going while still negative). Only the sign character needs special handling at the end.

Solution

/* K&R Exercise 3-4 — itoa handling INT_MIN
 * Compile: gcc -ansi -Wall ex3-4.c -o ex3-4 */
#include <stdio.h>
#include <string.h>
#include <limits.h>

void reverse(char s[])
{
    int i, j;
    char c;
    for (i = 0, j = strlen(s)-1; i < j; ++i, --j) {
        c = s[i]; s[i] = s[j]; s[j] = c;
    }
}

/* Original K&R itoa — undefined behaviour for INT_MIN */
void itoa_original(int n, char s[])
{
    int i, sign;
    if ((sign = n) < 0)
        n = -n;          /* UB if n == INT_MIN */
    i = 0;
    do {
        s[i++] = n % 10 + '0';
    } while ((n /= 10) > 0);
    if (sign < 0)
        s[i++] = '-';
    s[i] = '\0';
    reverse(s);
}

/* Fixed version — stays in the negative domain throughout */
void itoa(int n, char s[])
{
    int i, sign;
    sign = n;
    i = 0;
    do {
        s[i++] = -(n % 10) + '0';  /* n%10 <= 0 when n < 0 */
    } while ((n /= 10) < 0);
    if (sign < 0)
        s[i++] = '-';
    s[i] = '\0';
    reverse(s);
}

int main(void)
{
    char s[32];

    itoa(0, s);         printf("itoa(0)       = %s\n", s);
    itoa(12345, s);     printf("itoa(12345)   = %s\n", s);
    itoa(-12345, s);    printf("itoa(-12345)  = %s\n", s);
    itoa(INT_MAX, s);   printf("itoa(INT_MAX) = %s\n", s);
    itoa(INT_MIN, s);   printf("itoa(INT_MIN) = %s  (expected: %d)\n", s, INT_MIN);
    return 0;
}

Compile and Run

gcc -ansi -Wall ex3-4.c -o ex3-4
./ex3-4

Sample Output

itoa(0)       = 0
itoa(12345)   = 12345
itoa(-12345)  = -12345
itoa(INT_MAX) = 2147483647
itoa(INT_MIN) = -2147483648  (expected: -2147483648)

What This Exercise Teaches

  • Signed integer overflow is undefined behaviour — even negating INT_MIN is UB in C, not just a quirky result
  • Two’s complement asymmetry — the negative range is one wider than the positive range; INT_MIN has no positive counterpart
  • Working in the negative domain — extracting -(n % 10) avoids the overflow entirely
  • <limits.h> constantsINT_MIN and INT_MAX for portable boundary testing regardless of word size

Set Up Your C Environment

← Exercise 3-3  | 
Chapter 3 Solutions  | 
Exercise 3-5 →

Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>