Lets say I have 3 variables: a long
, an int
and a short
.
long l;
int i;
short s;
long lsum;
If this is a pure math, since multiplication has a commutative property, the order of these variables doesn't matter.
l * i * s = i * s * l = s * i * l.
Let lsum
be the container of the multiplication of these 3 variables.
In C, would there be a case where a particular order of these variables cause different result?
If there is a case where the order does matter, not necessarily from this example, what would that be?
The order does matter due to integer promotions.
When applying an arithmetic operator, each of its operands is first promoted to int
if its rank is less than int
(such as char
or short
). If one of those operands then has a higher rank still (such as long
), than the smaller is promoted.
From section 6.3.1 of the C standard:
2 The following may be used in an expression wherever an int or unsigned int may be used:
An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
From section 6.3.1.8:
If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
As an example (assuming sizeof(int)
is 4 and sizeof(long)
is 8):
int i;
short s;
long l, result;
i = 0x10000000;
s = 0x10;
l = 0x10000000;
result = s * i * l;
printf("s * i * l=%lx\n", result);
result = l * i * s;
printf("l * i * s=%lx\n", result);
Output:
s * i * l=0
l * i * s=1000000000000000
In this example, s * i
is evaluated first. The value of s
is promoted to int
, then the two int
values are multiplied. At this point an overflow occurs unvoking undefined behavior. The result is then promoted to long
and multiplied by l
, with the result being of type long
.
In the latter case, l * i
is evaluated first. The value of i
is promoted to long
, then the two long
values are multiplied and an overflow does not occur. The result is then multiplied by s
, which is first promoted to long
. Again, the result does not overflow.
In a situation like this, I'd recommend casting the leftmost operand to long
so that all other operands are promoted to that type. If you have parenthesized subexpressions you may need to apply a cast there as well, depending on the result you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With