【问题标题】:Does the order of multiplication variables of different data type cause different results?不同数据类型的乘法变量的顺序是否会导致不同的结果?
【发布时间】:2017-05-18 18:53:01
【问题描述】:

假设我有 3 个变量:longintshort

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

如果这是一个纯数学,由于乘法具有交换性质,这些变量的顺序无关紧要。

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

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

在 C 语言中,会不会出现这些变量的特定顺序导致不同结果的情况?

如果存在顺序确实重要的情况,不一定来自此示例,那会是什么?

【问题讨论】:

  • (s*i) 转换为 int,然后将 (s*i) * l 转换为 long。 (l*i) 转换为 long,然后 (l*i)* s 也转换为 long。如果 s*i 溢出 int_max,那么顺序会有所不同。
  • @aka.nice - 从您的 cmets 看来,类型提升是按顺序进行的,如果从小到大可能会溢出。如果是这样的话,一开始就从最大的类型开始是正确的做法。
  • @Nguaial:不会有溢出,但会有未定义的行为。 “大”是什么意思?不能保证int 小于long
  • @aka.nice "(s*i) 转换为 int" 不够清晰。 s 首先转换为 int。然后乘法发生。
  • @Nguaial Max of long is higher than max of int. 不完全是:LONG_MAX >= INT_MAX。那是>= 而不是>

标签: c integer-promotion


【解决方案1】:

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

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

来自C standard 的第 6.3.1 节:

2 以下可以在表达式中使用 int 或 unsigned int 的任何地方:

  • 具有整数类型(int 或 unsigned int 除外)的对象或表达式,其整数转换等级小于或等于 int 和 unsigned int 的等级。

  • _Bool、int、signed int 或 unsigned int 类型的位域。

如果一个 int 可以表示原始类型的所有值(受限制 通过宽度,对于一个位域),该值被转换为一个 int; 否则,它将转换为无符号整数。这些被称为 整数促销。所有其他类型都不变整数 促销活动。

来自第 6.3.1.8 节:

如果两个操作数的类型相同,则不再进行转换 需要。

否则,如果两个操作数都具有有符号整数类型或都具有 无符号整数类型,具有较小整数类型的操作数 转换等级转换为具有更大的操作数的类型 排名。

否则,如果无符号整数类型的操作数有秩 大于或等于另一个操作数的类型的等级,则 带符号整数类型的操作数转换为 无符号整数类型的操作数。

否则,如果带符号整数类型的操作数的类型可以 用无符号表示操作数类型的所有值 整数类型,然后转换无符号整数类型的操作数 为有符号整数类型的操作数的类型。

否则,两个操作数都转换为无符号整数类型 对应带符号整数类型的操作数的类型。

举个例子(假设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,以便将所有其他操作数提升为该类型。如果您有带括号的子表达式,您可能还需要在那里应用强制转换,具体取决于您想要的结果。

【讨论】:

    【解决方案2】:

    是的,参见http://www.cplusplus.com/articles/DE18T05o/上的“类型转换”和“类型提升”

    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 一样长的类型做的,结果是最长的操作数类型,是无符号的,所以结果是无符号的,会溢出。

    【讨论】:

    • 显然r2 是正确的答案。那么可以肯定地说总是从最大类型开始作为乘法顺序中的第一个变量吗?
    • @Nguaial 安全的方法是将第一个操作数转换为目标类型。
    • 为了安全,按照风向标所说的去做。如果最大的类型是第一个,您可能仍然永远不会扩展到足够宽的类型来容纳您的答案。例如unsigned long r0 = a * b 由于溢出,将具有值“1”。但是如果你unsigned long r0 = (unsigned long)a * b,你会得到扩展和预期的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 2017-01-07
    • 1970-01-01
    相关资源
    最近更新 更多