Exercise 3-4. In a two’s complement number representation, our version of
itoadoes not handle the largest negative number, that is, the value ofnequal 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_MINis UB in C, not just a quirky result - Two’s complement asymmetry — the negative range is one wider than the positive range;
INT_MINhas no positive counterpart - Working in the negative domain — extracting
-(n % 10)avoids the overflow entirely <limits.h>constants —INT_MINandINT_MAXfor portable boundary testing regardless of word size
Set Up Your C Environment
- Install GCC on Windows 11
- Install GCC on macOS
- Install GCC on Ubuntu/Linux
- VS Code for C Programming
← Exercise 3-3 |
Chapter 3 Solutions |
Exercise 3-5 →
Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie