Proven C Book한국어 GitHub

29 Implicit conversions — promotion and the usual arithmetic conversions

What to know first

chapter 26, The families of types · the word “arithmetic type”
chapter 27, Integers · integer types differ in width
chapter 28, Integer operations · an operation happens between one type

Looking back

Chapter 7 taught sign extension (widening 8 bits to 16), and the end of chapter 28 brushed past “the value in a smaller container is automatically widened before the calculation.” Then is the result type of char + char char?

A. No — it is int. And that fact is this chapter’s starting point. C’s arithmetic does not happen on small integer types. Everything is widened to int (or something larger) before the calculation, and the result is that wider type. Why that is, and how far it goes, is this chapter.

The need for this chapter, and its context

It matters that this chapter comes before comparison in chapter 30. Comparison, arithmetic and function calls all pass through silent conversions, and learning comparison without knowing that leaves no way to explain why a given comparison is true. Just after meeting integers of differing widths in chapters 27 and 28, and just before they start meeting each other — this is the only right slot.

By the end of this chapter

When C makes values of different types meet, it converts them silently. This chapter gathers those invisible conversions in one place — integer promotion, the usual arithmetic conversions, and the default promotions of variadic arguments. Scattered, each is a riddle; gathered, they are one system of rules.

The questions this chapter answers

  1. Must all these rules be memorised?

The words this chapter leans on throughout — arithmetic type, integer type, what integer promotion acts on — are as defined in chapter 26. If they blur, open those families again.

29.1 Rule 1 — integer promotion

Integer promotion: when a value of an integer type smaller than int — char, short, bool, a bit-field — takes part in an arithmetic operation, it is widened to int before the calculation (to int if int can hold all its values, otherwise to unsigned int).

examples-en/ch29/conv.c

#include <stdint.h>
#include <stdio.h>

int main(void)
{
    /* integer promotion: char and short are widened to int before the arithmetic */
    signed char a = 100;
    signed char b = 100;
    printf("100 + 100 (as chars) = %d\n", a + b);   /* 200 — a value a char cannot hold */

    /* the usual arithmetic conversions: the unsigned side wins —
       read -1 through unsigned eyes and it becomes a huge positive number */
    int neg = -1;
    printf("(unsigned)(-1)   = %u\n", (unsigned)neg);
    printf("so -1 < 1u is false\n");

    /* integer division vs real division — the cast states the intent */
    int total = 7, count = 2;
    printf("7 / 2        = %d\n", total / count);
    printf("(double)7/2  = %.1f\n", (double)total / count);

    /* the default promotions of variadic arguments: float to double, char/short to int */
    float f = 1.5f;
    printf("float 1.5f via %%f = %f  (promoted to double)\n", f);
    return 0;
}

Output

100 + 100 (as chars) = 200
(unsigned)(-1)   = 4294967295
so -1 < 1u is false
7 / 2        = 3
(double)7/2  = 3.5
float 1.5f via %f = 1.500000  (promoted to double)

The first line is the check — two signed char values of 100 were added and the result is 200. The reason a value that does not fit in a char did not overflow is that the addition happened not in char’s world but in int’s.

The reason lies in the machine (chapter 11). A CPU’s arithmetic circuits and registers are built to handle integers around the word size, so having separate arithmetic just for small types would be inefficient. C took that reality into the language as a rule — small types are units of storage, not units of calculation.

29.2 Rule 2 — the usual arithmetic conversions

If the two operands still differ in type after promotion, the usual arithmetic conversions settle on one common type. The order to remember in practice:

Spelled out in the standard’s own order, the integer rules are four steps.

conversions

Figure 29.1 — Follow the four in order. Most accidents happen at step 3.

That last line is the source of the trap. The second part of the demonstration is it in the flesh — read -1 through unsigned eyes and it becomes an enormous positive number over four billion (exactly chapter 7′s modular world). So a comparison like -1 < 1u comes out false, against intuition. That is why mixing signed values into array indices or size calculations (the result of sizeof is the unsigned size_t!) causes silent accidents.

Fortunately the compiler guards this trap well — the warnings switched on in chapter 17 point at sign-mixed comparisons (one example in this book was caught by that warning and rewritten). Reduced to a rule: do not mix signed and unsigned in one comparison. Use the size_t family consistently for sizes and indices, or state the intent with an explicit cast.

29.3 Rule 3 — default promotions for variadic arguments

The third conversion happens in functions whose argument count is not fixed — variadic functions such as printf. Arguments passed into a position where the prototype states no type undergo default argument promotions: float becomes double, and small integer types become int.

The demonstration’s last line is the check — a float value printed with %f comes out fine. The format %f in fact expects a double, and the float argument arrived as a double after promotion (which is why printf has no float-specific format at all). The “contract between format and materials” learned in chapter 22 has this promotion rule as a hidden clause — the full contract, and how to write variadic functions yourself, is faced head on in chapter 58.

In practice. The conversion that destroyed a rocket — Ariane 5, 1996

There is an event that shows how heavy implicit and explicit conversions can be. In 1996 the European Space Agency’s Ariane 5 rocket exploded 37 seconds1 after its first launch. The heart of the investigation’s finding was one line of conversion — the inertial navigation unit computed horizontal velocity as a 64-bit floating-point number, and there was code moving that value into a 16-bit signed integer. On the predecessor Ariane 4 that velocity never exceeded the 16-bit range and it was safe, but on the faster Ariane 5 the value overflowed its container. The failure of the narrowing conversion (chapter 7′s truncation) raised an exception, and with that exception unhandled the navigation computer stopped, whereupon the rocket lost attitude and self-destructed. The loss ran to hundreds of millions of dollars. Reused code meeting a new range of values broke the contract — the most expensive confirmation of this chapter’s sentence, that a conversion changes the value.

A common misconception. “A cast does not change the value, only the interpretation”

For pointer casts (chapter 37) that is broadly true, but a cast between arithmetic types changes the value itself. (int)3.9 becomes 3 (the fractional part discarded), (char)300 does not fit the container and is truncated (chapter 7′s narrowing), and (unsigned)-1 becomes an enormous positive number. C’s cast means not “read these bits as that type” but “convert this value into a value of that type” — if you want to leave the bits alone and change only the eye, chapter 48′s union or memcpy is that channel. Not writing the two demands with the same syntax is one of C’s few kindnesses.

Q. Must all these rules be memorised?

A. Three lines are enough — small integers are promoted to int; when mixed, the wider and the unsigned side wins; in variadic arguments float becomes double. Leave the rest of the detail to the appendix’s tables, and in practice two habits stand in for memorising rules: keeping warnings on (chapter 17), and stating a deliberate conversion with a cast. The danger of implicit conversion lies not in the complexity of the rules but in their being invisible, so making them visible is the best defence.

We have the families of conversions. From the next chapter come the tools of flow — beginning with the booleans and comparisons that turn judgement into a value.

Notes

  1. ARIANE 5 Flight 501 Failure: Report by the Inquiry Board. 1996. European Space Agency / CNES, Paris. sci.esa.int/web/cluster/-/38889