[英]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行必須進行類型轉換?
因為temp1
和temp2
的相加將導致該值超出32位unsigned
范圍,所以該結果的模2^32
的模降低將更改整個結果。
首先使用“較大”類型(即,不要將a
聲明為unsigned short
,而只需將其聲明為unsigned int
)。 如果您需要強制轉換,那么,只需將操作的一側轉換即可。 促銷將為您進行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.