不同数据类型的乘法变量的顺序是否会导致不同的结果?

Does the order of multiplication variables of different data type cause different results?

假设我有 3 个变量:longintshort

long  l; 
int   i;
short s;
long  lsum;

如果这是纯数学,因为乘法具有交换律属性,这些变量的顺序无关紧要。

 l * i * s = i * s * l = s * i * l.

lsum为这3个变量相乘的容器。

在 C 中,是否存在这些变量的特定顺序导致不同结果的情况?

如果有顺序确实重要的情况,不一定来自这个例子,那会是什么?

由于整数促销,顺序确实很重要。

当应用算术运算符时,如果其操作数的秩小于 int(例如 charshort),则其每个操作数首先提升为 int .如果这些操作数之一仍然具有更高的等级(例如 long),则提升较小的。

来自 C standard 的第 6.3.1 节:

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.

来自第 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.

举个例子(假设sizeof(int)是4,sizeof(long)是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);

输出:

s * i * l=0
l * i * s=1000000000000000

在此示例中,首先评估 s * is的值提升为int,然后两个int的值相乘。此时发生溢出,取消未定义的行为。然后将结果提升为 long 并乘以 l,结果类型为 long.

在后一种情况下,首先计算 l * ii 的值被提升为 long,然后两个 long 的值相乘, 不会 发生溢出。然后将结果乘以 s,首先提升为 long。同样,结果不会溢出。

在这种情况下,我建议将最左边的操作数转换为 long,以便所有其他操作数都提升为该类型。如果您有带括号的子表达式,您可能还需要在那里应用强制转换,具体取决于您想要的结果。

是的,参见 http://www.cplusplus.com/articles/DE18T05o/ "Type conversion" 和 "Type promotion"

unsigned a = INT_MAX;
unsigned b = INT_MAX;
unsigned long c = 255;

unsigned long r1 = a * b * c;
unsigned long r2 = c * a * b;

r1=255 r2=13835056960065503487

r1 反映了(a*b)首先完成的类型至少和int一样长,结果是最长的操作数类型,是无符号的,所以结果是无符号的并且溢出。