簡體   English   中英

C類型轉換:如何

[英]C Typecast: How to

#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行必須進行類型轉換?

執行溢出操作之前,您必須提升類型。 在第8行中將是乘法,所以

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

注意,僅將對稱運算中的一個操作數提升為*就足夠了。 您可以根據需要使用(unsigned) a * (unsigned) a ,但也可以使用(unsigned) a * (unsigned) a (unsigned) a * a

這將照顧乘法,它將不再溢出。 但是,現在添加將溢出。 盡管32位unsigned對於a * a是足夠a * a ,但對於a * a + 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 = a * a; 線。 由於相同的原因,它們將溢出。 你必須做

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

以避免溢出,即促進a乘法之前

這正是您在第14行中正確執行的操作,即加法之前提升+操作數,盡管僅提升一個操作數就足夠了

temp3 = (unsigned long) temp1 + temp2;

我該如何修正算術(在第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。

這是因為,如果int是(很可能是)二進制補碼32位整數類型,且溢出時具有環繞行為,則計算結果為-2 [有符號整數的溢出是未定義的行為,因此,編譯器優化可以改變它; 如果編譯器分析了計算結果並發現它是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值都可以表示為int s),並且該算術以int類型執行。

然后,通過USHRT_MAX +1添加USHRT_MAX +1將結果-2轉換為unsigned short ,導致USHRT_MAX - 1之后為e的值。

為什么第14行必須進行類型轉換?

因為temp1temp2的相加將導致該值超出32位unsigned范圍,所以該結果的模2^32的模降低將更改整個結果。

首先使用“較大”類型(即,不要將a聲明為unsigned short ,而只需將其聲明為unsigned int )。 如果您需要強制轉換,那么,只需將操作的一側轉換即可。 促銷將為您進行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM