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 行目)。現在、予想される 65535 ではなく 65534 が出力されます。

Line 14 に typecast が必要なのはなぜですか?

4

3 に答える 3

2

オーバーフロー操作を実行する前に、型をプロモートする必要があります。8行目では乗算になるので、

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

*十分な対称操作の1つのオペランドのみをプロモートすることに注意してください。(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); 

(ここでも、の最初のオペランドのみをプロモートするだけで+十分です。つまり、2番目の乗算をに残すことができます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行目で正しく行うことです。つまり、1つのオペランドだけをプロモートするだけで十分ですが、加算+ 前のオペランドをプロモートします。

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

オーバーフローが処理されるように、算術演算を修正するにはどうすればよいですか(中間結果の適切な型キャストによる8行目)。

分子内の両方の乗算の一方のパートナーを十分な大きさの型にキャストする必要があります。、分子は32ビットの符号なし整数をオーバーフローするため(2^16-1)² = 2^32 - 2^17 + 1、より大きなものが必要になります(通常は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して、持ち運び可能で安全なものにします。

現在、予想される65535ではなく65534を出力します。

これは、計算の結果が、-2オーバーフローint時のラップアラウンド動作を伴う2の補数の32ビット整数型であるためです[ただし、符号付き整数のオーバーフローは未定義の動作であるため、コンパイラの最適化によって変更される可能性があります。コンパイラーが計算を分析し、それがそれであると判断した場合、オーバーフローが発生しないゼロ以外の結果になるため、2*a*a/(2*a)単純化することができます。ゼロ除算もUBであるため、すべてのケースがカバーされます]。aa

(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値をsとして表すことができるためint)、算術はタイプ.で実行されintます。

結果は、それに追加することによって-2変換され、その後の値になります。unsigned shortUSHRT_MAX +1USHRT_MAX - 1e

14行目に型キャストが必要なのはなぜですか?

とを加算するtemp1temp232ビットunsigned範囲外の値になるため、その結果を法として除算する2^32と、全体的な結果が変化します。

于 2012-12-07T23:08:04.017 に答える
0

最初に「より大きな」型を使用します (つまり、 として宣言せずa、 としてunsigned short宣言するだけunsigned intです)。キャストする必要がある場合は、操作の片側だけをキャストしてください。プロモーションはあなたのために発生します。

于 2012-12-07T22:09:36.537 に答える