簡體   English   中英

無符號整數中的上溢和下溢

[英]Overflow and underflow in unsigned integers

假設,我試圖減去 2 個無符號整數:

247 = 1111 0111
135 = 1000 0111

如果我們減去這 2 個二進制數,我們得到 = 0111 0000

這是下溢嗎,因為我們現在只需要 7 位? 或者它是如何工作的?

下溢的無符號減法c = a - b每當發生b大於a

然而,這有點循環定義,因為有多少種機器執行a < b比較是通過使用環繞算法減去操作數,然后根據兩個操作數和結果檢測溢出。

另請注意,在 C 中我們不談論“溢出”,因為沒有錯誤條件:C 無符號整數提供了硬件中常見的環繞算法。

因此,鑒於我們有環繞算法,我們可以檢測在減法中是否發生了環繞(或溢出,取決於觀點)。

我們需要的是abc最重要的位。 我們稱它們為ABC 根據這些,溢出V計算如下:

A B C | V
------+--
0 0 0 | 0
0 0 1 | 1
0 1 0 | 1
0 1 1 | 1
1 0 0 | 0
1 0 1 | 0
1 1 0 | 0
1 1 1 | 1

這簡化為

A'B + A'C + BC

換句話說,無符號減法c = a - b中的溢出發生在:

  1. 的MSB a為0,並且的b為1;
  2. 或的最高位a為0,並且的c是1;
  3. 或者b的最高位元為 1,而c的最高位元也為 1。

減去 247 - 135 = 112 顯然不會溢出,因為 247 大於 135。應用上面的規則,A = 1,B = 0 和 C = 0。表的 1 1 0 行在 V 列中有一個 0 : 沒有溢出。

通常,“下溢”意味着計算的理想數學結果低於該類型可以表示的結果。 如果在無符號算術中從 5 中減去 7,理想的數學結果將是 -2,但無符號類型不能表示 -2,因此運算下溢。 或者,在可以表示從 -128 到 +127 的數字的八位有符號類型中,從 -100 中減去 100 會產生 -200,但這無法在類型中表示,因此運算下溢。

在 C 中,無符號算術被稱為不會下溢或溢出,因為 C 標准定義了要使用模算術而不是實數算術執行的運算。 例如,對於 32 位無符號算術,從 5 中減去 7 將產生 4,294,967,294(十六進制為 FFFFFFFE 16 ),因為它包含了模 2 32 = 4,294,967,296。 盡管如此,人們在討論這些操作時可能會使用術語“下溢”或“溢出”,旨在指代數學問題而不是定義的 C 行為。

換句話說,對於您用於算術的任何類型,都有該類型可以表示的某個下限L和某個上限U。 如果運算的理想數學結果小於L ,則運算下溢。 如果運算的理想數學結果大於U ,則運算溢出。 “下溢”和“溢出”意味着操作已經超出了類型的界限。 “溢出”也可用於指任何超出類型邊界的情況,包括在低方向。

這並不意味着需要更少的位來表示結果。 當從 11110111 2 中減去 10000111 2 時,結果 01110000 2 = 1110000 2在界限內,因此沒有上溢或下溢。 它需要更少的位來表示這一事實無關緊要。

(注意:對於整數算術,“下溢”或“溢出”是相對於絕對邊界LU 定義的。對於浮點算術,這些術語的含義有些不同。它們可能是相對於結果的大小來定義的,忽略符號,它們是相對於格式的有限非零范圍定義的。浮點格式可能能夠表示 0,然后是各種有限的非零數,然后是無窮大。0 和最小之間的某些結果格式可以表示的非零數被稱為下溢,即使它們在技術上處於可表示數的范圍內,該范圍是從 0 到無窮大。類似地,某些高於最大可表示有限數的結果被稱為溢出,即使它們在可表示范圍內,因為它們小於無窮大。)

長話短說,這就是當您有以下情況時會發生的情況:

unsigned char n = 255; /* highest possible value for an unsigned char */

n = n + 1; /* now n is "overflowing" (although the terminology is not correct) to 0 */
printf("overflow: 255 + 1 = %u\n", n);
   
n = n - 1; /* n will now "underflow" from 0 to 255; */
printf("underflow: 0 - 1 = %u\n", n);
   
n *= 2; /* n will now be (255 * 2) % 256 = 254; 
        /* when the result is too high, modulo with 2 to the power of 8 is used */ 
        /* for an 8 bit variable such as unsigned char; */
printf("large overflow: 255 * 2 = %u\n", n);   
   
n = n * (-2) + 100; /* n should now be -408 which is 104 in terms of unsigned char. */
                       /* (Logic is this: 408 % 256 = 152; 256 - 152 = 104) */
printf("large underflow: 255 * 2 = %u\n", n);

結果是(用 gcc 11.1 編譯,標志 -Wall -Wextra -std=c99):

 overflow: 255 + 1 = 0 underflow: 0 - 1 = 255 large overflow: 255 * 2 = 254 large underflow: 255 * 2 = 104

現在是科學版本:上面的評論僅代表正在發生的事情的數學模型。 為了更好地理解實際發生的情況,以下規則適用:

  1. 整數提升:

當對它們執行操作時,會提升小於 int 的整數類型。 如果原始類型的所有值都可以表示為int,則將較小類型的值轉換為int; 否則,它被轉換為無符號整數。

那么當計算機n = 255; n = n + 1;時,內存中實際發生了什么n = 255; n = n + 1; n = 255; n = n + 1; 是這樣的:首先,右邊被評估為一個int(有符號),因為根據整數提升的規則,結果適合一個有符號的int。 所以表達式的右邊變成了二進制: 0b00000000000000000000000011111111 + 0b00000000000000000000000000000001 = 0b00000000000000000000000100000000

  1. 截斷

將 32 位 int 分配回 8 位數字時,會丟失最高有效的 24 位。

因此,當將0b00000000000000000000000100000000分配給變量 n(一個無符號字符)時,32 位值被截斷為 8 位值(僅復制最右邊的 8 位)=> n 變為0b00000000

每個操作都會發生同樣的事情。 右側的表達式計算為有符號整數,然后將其截斷為 8 位。

暫無
暫無

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

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