Exercises — Data types — char, short, int, long, float, double, size_t
This page is a staircase: we start by recognising the types, then apply the range formulas, then analyse tricky overflow/float traps, then synthesise multi-step programs, and finally reach mastery problems that combine everything. Every symbol used here was built in the parent note the parent Data Types note — if you want the "from zero" build-up of , two's complement, or the IEEE-754 formula, read that first.
Before we count anything, one picture fixes the vocabulary we lean on the whole page.

The two formulas we grind against, both derived in the parent note:
Level 1 — Recognition
Goal: read a type and immediately recall its size, its family (integer vs float), and whether it is signed.
L1.1
State: Which single data type has a sizeof that the C standard guarantees to equal ?
Recall Solution — L1.1
What we're asked: the one type whose byte-size is fixed by the standard (not by platform).
Answer: char. Every other size (int, long, size_t, …) is only bounded by the standard, not pinned. By definition sizeof(char) == 1 — the byte is the unit of sizeof. See sizeof Operator.
L1.2
State: For each of unsigned int, size_t, signed char, double — say whether it can hold a negative value.
Recall Solution — L1.2
unsigned int→ no (all patterns are non-negative; range starts at ).size_t→ no (it is unsigned — a size can never be below ).signed char→ yes (top bit carries negative weight in two's complement).double→ yes (it has a dedicated sign bit in ).
L1.3
State: Order these by typical size on a 64-bit Linux (LP64), smallest first: long, char, int, short.
Recall Solution — L1.3
char (1) short (2) int (4) long (8).
Why: this matches the standard's guaranteed ordering . Sizes are bytes; the picture of stacked boxes in Figure s01 shows why bigger boxes hold bigger numbers.
Level 2 — Application
Goal: plug numbers into the two range formulas and read off exact bounds.
L2.1
State: Compute the exact range of a signed char ().
Recall Solution — L2.1
What we do: substitute into the signed formula. Why vs : there are negative patterns but only strictly-positive ones, because zero uses a slot on the non-negative side. The asymmetry is not a bug — it is the two's-complement design.
L2.2
State: Compute the range of unsigned short ().
Recall Solution — L2.2
What we do: unsigned formula with . Why not : counts the patterns; subtract because pattern number one is the value .
L2.3
State: What is INT_MAX and INT_MIN for a 32-bit int?
Recall Solution — L2.3
Signed formula, :
Why it matters: adding to INT_MAX is undefined behaviour in signed arithmetic — see Integer Overflow and Undefined Behaviour.
L2.4
State: How many distinct values can a 4-bit unsigned "nibble" hold, and what is its max?
Recall Solution — L2.4
Count of patterns . Max value . So the range is — exactly 16 values, matching Figure s02's little clock of 16 positions.
Level 3 — Analysis
Goal: predict what a program prints when wrap-around, conversion, or float rounding bites.
L3.1 — signed wrap
State:
signed char c = 127;
c = c + 1;
printf("%d\n", c);Predict the output.
Recall Solution — L3.1
What happens: is 0111 1111. Adding gives 1000 0000. Read as two's complement, 1000 0000 is the most-negative pattern .
Why: the clock in Figure s02 wraps — one tick past the highest positive lands on the lowest negative.
Output: -128.
(Note: at wider int width this addition would be fine; the wrap here happens because c is stored back into an 8-bit signed char. Signed overflow at full int width is UB, see Integer Overflow and Undefined Behaviour.)
L3.2 — the char=200 classic
State:
char c = 200; // assume char is signed
unsigned char u = 200;
printf("%d %d\n", c, u);Predict.
Recall Solution — L3.2
Pattern: 1100 1000.
- As
unsigned char: value is literally . - As signed
char: top bit set ⇒ value . Why : the two's-complement rule "if top bit is 1, subtract " with . See Type Conversion and Promotion for why the same bits get two readings. Output:-56 200.
L3.3 — the float equality trap
State: Does 0.1 + 0.2 == 0.3 evaluate to true in C?
Recall Solution — L3.3
No. Neither nor has a finite binary fraction (just as has no finite decimal). Each is rounded to the nearest representable double, and the rounded sum is 0.30000000000000004, which differs from the rounded 0.3.
Fix: compare fabs((0.1+0.2) - 0.3) < 1e-9. Details in IEEE-754 Floating Point.
L3.4 — the unsigned countdown
State:
for (size_t i = 3; i >= 0; i--)
printf("%zu ", i);What happens?
Recall Solution — L3.4
Infinite loop. size_t is unsigned, so i >= 0 is always true. After i == 0, i-- computes 0u - 1, which wraps to the maximum size_t (e.g. ), and the loop never ends. It prints 3 2 1 0 then a giant number, then keeps running.
Fix: loop with a signed index, or use for (size_t i = 3; i-- > 0; ) idiom.
Level 4 — Synthesis
Goal: chain several ideas — bit layout, conversion, precision — into one answer.
L4.1 — decimal digits of float
State: From its bit layout, derive how many significant decimal digits a float carries.
Recall Solution — L4.1
Step 1 (WHAT): a float has explicit mantissa bits plus implicit leading bit bits of significand.
Step 2 (WHY the log): each binary bit multiplies the number of representable values by ; to convert "how many bits" into "how many decimal digits" we ask how many powers of ten fit in that many powers of two — that is exactly decimal digits per bit.
Step 3: ⇒ ~7 significant decimal digits.
For double: ⇒ ~15–16 digits.
L4.2 — encode a value in IEEE-754 float
State: Give sign , stored exponent , and the value of for the number in 32-bit float (bias ).
Recall Solution — L4.2
Step 1 (sign): so . Step 2 (normalize): , so the significand is (i.e. ) and the true exponent is . Step 3 (bias it): stored . Check: . ✅ Formula recalled: , from IEEE-754 Floating Point.
L4.3 — promotion in a mixed expression
State:
unsigned int a = 1;
int b = -2;
printf("%s\n", (a + b > 0) ? "positive" : "not");What prints, and why?
Recall Solution — L4.3
What: in a + b, the int b is converted to unsigned int (usual arithmetic conversions promote the signed operand to unsigned). -2 becomes .
Then: (mod ), which is .
Output: positive.
Why the surprise: you expected , but signed→unsigned conversion happened before the comparison. See Type Conversion and Promotion.
Level 5 — Mastery
Goal: reason about limits, degenerate inputs, and full programs where several traps stack.
L5.1 — the most-negative has no positive twin
State: For 8-bit signed char, what does -INT_MIN-style negation do? Concretely, what is the value of -(signed char)(-128) when kept in a signed char?
Recall Solution — L5.1
What: is 1000 0000. To negate in two's complement: invert bits → 0111 1111, add → 1000 0000. We got the same pattern back!
Why: is not representable in signed char (max is ). The most-negative value is its own negation modulo . So -(-128) stays -128.
Lesson: the range asymmetry () means "negate" is not always safe. At full int width, -INT_MIN is UB — Integer Overflow and Undefined Behaviour.
L5.2 — precision gap of float near a big number
State: Around , a 32-bit float cannot represent every integer. What is the spacing between consecutive representable floats there, and hence: does (float)16777216 + 1 equal 16777217?
Recall Solution — L5.2
Step 1: with significand bits, consecutive floats near are spaced apart (the last mantissa bit has weight ).
Step 2: at : spacing . So representable values step by : — the odd integer is skipped.
Step 3: (float)16777216 + 1 rounds to the nearest representable value. is exactly halfway... actually it rounds to the even neighbour .
Answer: (float)16777216 + 1 == 16777216, not . Precision is relative: the higher you go, the coarser the grid (Figure s03).
L5.3 — zero and degenerate width
State: (a) What is the range of a hypothetical -bit signed integer? (b) What is when , and does a -bit type make sense?
Recall Solution — L5.3
(a) signed: . Just two values: 1 means , 0 means . There is no positive value — all the asymmetry collapsed onto the single negative.
(b) : — exactly one pattern (the empty pattern), which can only encode the value . C has no -bit type, but the formula stays consistent: one pattern, range . This is the degenerate limit that confirms the counting principle never breaks.
L5.4 — full program forecast
State:
#include <stdio.h>
int main(void) {
unsigned char x = 250;
x += 10; // (A)
signed char y = x; // (B) assume signed char, value-preserving mod 256
printf("%u %d\n", x, y);
return 0;
}Forecast the two printed numbers.
Recall Solution — L5.4
(A): unsigned char range is . . Unsigned arithmetic wraps mod : . So x == 4.
(B): 4 fits in signed char , so y == 4 (no re-interpretation needed).
Output: 4 4.
Why (A) is defined but signed overflow isn't: unsigned wrap-around is specified by the standard (mod ); signed overflow is UB. This is the single most important distinction from Integer Overflow and Undefined Behaviour.
Active Recall
Recall One-line self-test (cover the answers)
signed charrange? →unsigned shortmax? →(char)200as signed? →floatsignificant digits & why? → ~7, from- Spacing of floats near ? →
250u + 10inunsigned char? → (wraps mod 256)-(signed char)(-128)? → still