0
#include<stdio.h>
int main(void)
{
    unsigned short a,e,f ;    // 2 bytes data type
    unsigned int temp1,temp2,temp4; // 4 bytes data type
    unsigned long temp3; // 8 bytes data type
    a=0xFFFF;
    e=((a*a)+(a*a))/(2*a); // Line 8
    //e=(((unsigned long)(a*a)+(unsigned long)(a*a)))/(unsigned int)(2*a);    

    temp1=a*a;
    temp2=a*a;
    temp3=(unsigned long)temp1+(unsigned long)temp2; // Line 14
    temp4=2*a;

    f=temp3/temp4;

    printf("%u,%u,%lu,%u,%u,%u,%u\n",temp1,temp2,temp3,temp4,e,f,a);
    return(1);
}

如何修复算术(在第 8 行通过适当的中间结果类型转换)以便处理溢出?目前它打印 65534 而不是预期的 65535。

为什么第 14 行需要类型转换?

4

3 回答 3

2

您必须在执行溢出操作之前提升类型。在第 8 行中,这将是乘法,所以

e = ((unsigned) a * a + (unsigned) a * a) / (2 * (unsigned) a); 

请注意,仅提升此类对称操作的一个操作数*就足够了。您可以根据需要使用(unsigned) a * (unsigned) a,但(unsigned) a * a也可以使用。

这将处理乘法,不再溢出。但是,现在添加将溢出。虽然 32 位unsigned足以a * a满足a * a + a * a. 为此,您将需要unsigned long(假设它更大)。您可以正式提升 to 的第一个操作+unsigned long

e = ((unsigned long) ((unsigned) a * a) + (unsigned) a * a) / (2 * (unsigned) a); 

(同样,只提升 的第一个操作数+就足够了,这意味着第二个乘法可以留在 中unsigned)。

上面看起来有点太复杂了,为了让它看起来更干净,你可以unsigned long从一开始就在第一个乘法中使用

e = ((unsigned long) a * a + (unsigned) a * a) / (2 * (unsigned) a); 

或者你可以在unsigned long任何地方使用,让它看起来更干净

e = ((unsigned long) a * a + (unsigned long) a * a) / (2 * (unsigned long) a); 

同样的问题出现在您的temp1 = a * a;行中。出于同样的原因,它们会溢出。你所要做的

temp1 = (unsigned) a * a;
temp2 = (unsigned) a * a;

避免溢出,即在乘法a 之前提升。

这正是您在第 14 行正确执行的操作,即提升+ 之前加法的操作数,尽管仅提升一个操作数就足够了

temp3 = (unsigned long) temp1 + temp2;
于 2012-12-07T23:01:44.043 回答
1

如何修复算术(在第 8 行通过适当的中间结果类型转换)以便处理溢出?

您需要将分子中两个乘法的一个伙伴转换为足够大的类型。由于(2^16-1)² = 2^32 - 2^17 + 1,分子会溢出 32 位无符号整数,因此您需要更大的值(通常是 64 位类型)。如果(unsigned) long满足尺寸要求,

e = (((unsigned long)a*a)+((unsigned long)a*a))/(2*a);

将是定义明确且安全的(也可以使用强制转换为long,这里只需要 33 位)。

但是usingunsigned long是不可移植的,因为它可能是 32 位类型。使用(unsigned) long long(u)int_least64_t便携且安全。

目前它打印 65534 而不是预期的 65535。

那是因为计算的结果是-2ifint是(很可能是)一个二进制补码 32 位整数类型,在溢出时具有环绕行为 [不过,有符号整数的溢出是未定义的行为,因此编译器优化可能会改变它;如果编译器分析计算并看到它2*a*a/(2*a)可以将其简化为,因为这将是任何不发生溢出a的非零的结果;a除以 0 也是 UB,因此涵盖了所有情况]。

(2^16-1)² = 2^32 - 2^17 + 1 ≡ -2^17 + 1 = -131071 (mod 2^32)
(-131071) + (-131071) = -262142
(-262142)/131070 = -2

通常的算术转换应用于 , 和 的操作数*+因此/操作unsigned short数被转换为int(因为这里所有unsigned short值都可以表示为ints),并且算术在 type 处执行int

然后通过将结果添加到它来-2转换为,结果是之后的值。unsigned shortUSHRT_MAX +1USHRT_MAX - 1e

为什么第 14 行需要类型转换?

因为加上temp1andtemp2会产生一个超出 32 位unsigned范围的值,所以该结果的约简模数2^32会改变整体结果。

于 2012-12-07T23:08:04.017 回答
0

您使用“更大”类型开始(即,不要声明aunsigned short,只需将其声明为unsigned int)。如果您需要施放,那么,只需施放操作的一侧。促销活动将为您进行。

于 2012-12-07T22:09:36.537 回答