C Typecast:如何

时间:2012-12-07 22:05:01

标签: c casting overflow

#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行必须进行类型转换?

3 个答案:

答案 0 :(得分: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(假设它更大)。您可以正式将+的第一个操作数提升为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;

答案 1 :(得分: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位)。

然而,使用unsigned long不可移植,因为它可能是32位类型。使用(unsigned) long long(u)int_least64_t可移植且安全。

  

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

这是因为如果-2int(很可能是)一个二进制补码32位整数类型,在溢出时具有环绕行为,则计算结果为2*a*a/(2*a) [有符号整数的溢出但是,是未定义的行为,因此编译器优化可能会改变它;如果编译器分析计算并看到它是a,它可以将它简化为a,因为这将是任何非零(2^16-1)² = 2^32 - 2^17 + 1 ≡ -2^17 + 1 = -131071 (mod 2^32) (-131071) + (-131071) = -262142 (-262142)/131070 = -2 的结果,其中不会发生溢出;除以0也是UB,所以所有情况都被覆盖了。

*

通常的算术转换应用于+/unsigned short的操作数,因此int个操作数转换为unsigned short(因为这里所有int值都可以表示为int s),算术是在-2类型上执行的。

结果unsigned short通过向其添加USHRT_MAX +1转换为USHRT_MAX - 1,从而导致e之后成为temp1的值。

  

为什么第14行必须进行类型转换?

因为添加temp2unsigned会导致值超出32位2^32范围,因此该结果的减少模{{1}}会改变总体结果。

答案 2 :(得分:0)

您首先使用“较大”类型(即,不要将a声明为unsigned short,只需将其声明为unsigned int)。如果你需要施放,那么,只需施放一个操作的一侧。促销活动将在您身上进行。