In C 1999 note 86, speaking to 6.5.4 5 (my bold)
If the value of the expression is represented with greater precision or range than required by the type named by the cast (6.3.1.8), then the cast specifies a conversion even if the type of the expression is the same as the named type.
signed char a = -2;
unsigned char b = 1;
b = (short)a + (short)b;
b = (short)a + (short)b;
. This is an example of
If the value of the expression is represented with greater precision or range than required by the type named by the cast (6.3.1.8),
Because if there isn't cast, a
and b
both are promoted to the int
, which is greater precision or range than short
which is the type named by the cast (suppose that short
is 16 bit and int
is 32 bit).
Then, in this case, the latter sentence
then the cast specifies a conversion even if the type of the expression is the same as the named type.
I want to know how this affects b = (short)a + (short)b;
.
My thought was simple. Because (cast)
has higher precedence than additive +
, first a
and b
are casted by (short)
and then two operands of +
are converted by the usual arithmetic conversion. But the sentence "then the cast specifies a conversion even if the type of the expression is the same as the named type" confuses me.
This is about floating-point types. Integer types are not affected.
b = (short)a + (short)b;. This is an example of “If the value of the expression is represented with greater precision or range than required…”
No, the integer casts in that statement are not an example of that. C 2018 6.3.1.8 2 (same in C 1999) tells you it is about floating-point types:
The values of floating operands and of the results of floating expressions may be represented in greater range and precision than that required by the type; the types are not changed thereby.
What is happening here is that the C standard allows floating-point expressions to be computed with more range and precision than their nominal types, per C 2018 5.2.4.2.2 10:
Except for assignment and cast (which remove all extra range and precision), the values yielded by operators with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type…
(In C 1999, similar text appears at 5.4.2.2.2 7. The differences may have some significance but are not discussed here.)
This means that when you compute an expression with double
, the compiler might, for example, generate code that uses long double
. Or, when you compute an expression with float
, the compiler might use double
or long double
. This makes it easier, for example, to implement C on a processor that has instructions for loading and storing single-precision (float
) or double-precision (double
) floating-point data but that has instructions for computing only double-precision floating-point, not for single-precision.
The note 86 you cited from C 1999 has been promoted to normative text in C 2018, as 6.5.4 6:
If the value of the expression is represented with greater range or precision than required by the type named by the cast (6.3.1.8), then the cast specifies a conversion even if the type of the expression is the same as the named type and removes any extra range and precision.
What this is telling you is that a cast must “remove” that extra range and precision. (This is unfortunate phrasing; it would be better to say that the value is rounded to a value representable in the named type using whatever rounding rule applies, commonly round-to-nearest ties-to-even.)
In C 2018 6.3.1.5 1 (not present in C 1999), about conversions between floating-point types, we see:
… Results of some implicit conversions may be represented in greater range and precision than that required by the new type (see 6.3.1.8 and 6.8.6.4).
So the situation is this: Floating-point expressions may be computed using extra range and precision. When there is an implicit conversion, such as when multiplying a float
by a double
(the usual arithmetic conversions convert the float
to double
), this extra range and precision may remain. However, when you have a cast in the source code, an “actual conversion” must be performed that converts the value with extra precision to a value representable in the nominal type. This is what it means to say that the cast specifies a conversion.
Although the conversion in assignment is implicit, assignments are also required to perform this conversion, as we are informed in 5.2.4.2.2 10, quoted above. That is also noted in note 65 to 6.3.1.8 2:
The cast and assignment operators are still required to remove extra range and precision.
It may be of note that a return
statement performs an implicit conversion but is not required to remove extra range or precision.