溢出和進位

溢出與進位

於無符號數來說,不存在溢出的問題,它的進位就相當於有符號數中的溢出
而對有符號數來說,不存在進位的問題

一個字節(8位) 的數有256個 (2的8次方)
一個字(16位) 的數有65536個 (2的16次方)
8個二進制位能夠表達的無符號數範圍是:0 ~ 255
16
位表達的無符號數範圍是:0 ~ 65535
對於無符號數來說,不存在溢出的問題,它的進位就相當於有符號數中的溢出.
進位表示最高位有沒有向上形成進位,或向個形成借位,如果有則進位標誌CF爲進/借位數,但結果沒錯

    進/借位數要看 十六進制、二進制,進/借的是十六進制、二進制的最高位處理器內部以補碼錶示有符號數,

8個二進制位能夠表達的有符號數範圍是:+127 ~ -128

16位表達的有符號數範圍是:+32767 ~ -32768

    如果運算結果超出了這個範圍,就是產生了溢出,有溢出,說明有符號數的運算結果不正確 

    溢出是指結束超出數據所表示的範圍,通俗地說是裝不下了,比如,兩個帶符號的字節數127和2相加,結果爲-1,因爲帶符號的字節數最大正數爲127,所以超過範圍,溢出標誌位OF爲1,說明出錯了。

   對於有符號數來說,不存在進位的問題

    例如:

    3AH + 7CH=B6H,就是58 + 124=182,已經超出-128 ~ 127範圍,產生溢出,所以OF=1;另一方面,補碼B6H表達真值是-74,顯然運算結果也不正確。

    溢出標誌OF和進位標誌CF是兩個意義不同的標誌.

n        進位標誌表示無符號數運算結果是否超出範圍,運算結果仍然正確;

n        溢出標誌表示有符號數運算結果是否超出範圍,運算結果已經不正確

    請看例子

例1:3AH + 7CH=B6H
無符號數運算:58+124=182,範圍內,無進位
有符號數運算: 58+124=182 ,範圍外,有溢出
例2:AAH + 7CH=(1)26H
無符號數運算:170+124=294,範圍外,有進位
有符號數運算:-86+124=28 ,範圍內,無溢出
    處理器對兩個操作數進行運算時,按照無符號數求得結果,並相應設置進位標誌CF;同時,根據是否超出有符號數的範圍設置溢出標誌OF。

    應該利用哪個標誌,則由程序員來決定。也就是說,如果將參加運算的操作數認爲是無符號數,就應該關心進位;認爲是有符號數,則要注意是否溢出。

    判斷運算結果是否溢出有一個簡單的規則:只有當兩個相同符號數相加,而運算結果的符號與原數據符號相反時,產生溢出,此時的運算結果顯然不正確。其他情況下,則不會產生溢出。

    兩個正數相加(或一個正數減一個負數)得到負數,或是兩個負數相加得到正數,就是溢出了.

一個正數和一個負數相加不可能溢出

 

 

進位與溢出

進位與溢出

       Cy位是進位位,用來表示本次無符號數運算結果的溢出。由於無符號數的最高有效位只有數位意義而無符號意義,所以該位所產生的進位應該是本次運算結果的實際進位值。所以說:進位位Cy是在給定二進制數的位數範圍內,代表了本次運算結果的溢出情況。另一方面,它所保存的進位值有時也是有用的。例如,雙字長運算時,可以利用進位值把低位字的進位計入高位字。

       OV位表示溢出。溢出位是用來表示帶符號數的運算結果超出有限字長的表示範圍的標誌。它是根據兩個操作數的符號及其變化來設置的。如兩個操作數符號相同而運算結果的符號與之相反時OV=1,反之,OV=0。

       例題1:無符號數和帶符號數均不溢出

 

按無符號數對待

按帶符號數對待

0000 0100

4

(+) 4

+     0000 1011

     +  11

    +   (+)11

0000 1111

15

(+)15

       例題2:無符號數溢出的情況

 

按無符號數對待

按帶符號數對待

0000 0111

7

(+)7

+  1111 1011

     +  251

    +   (-)5

1Cy 0000 0010

258

(+)2

 

Cy = 1

OV =0

       注:在字長爲8位的情況下,258表示的也是2,所以結果均爲2。

       例題3:帶符號數溢出的情況

 

按無符號數對待

按帶符號數對待

0000 1001

9

(+)9

+  0111 1100

     +  124

    +   (+)124

  1000 0101

133

(+)133

 

Cy =0

OV = 1

       例題3:帶符號數和無符號數均溢出的情況

 

按無符號數對待

按帶符號數對待

1000 0111

135

(-)121

+  1111 0101

     +  245

    +   (-)11

  1Cy 0111 1100

380

(-)132

 

Cy =1

OV = 1

       結論:兩個同符號數相加,纔可能產生溢出。兩個符號相異的數相加不可能產生溢出。計算機對進位位的判斷規則爲:兩個帶符號數進行補碼加減運算時,通常用符號位產生的進位(S代表)與最高有效數值位向符號位產生的進位(Cy代表)進行異或操作,若異或結果爲1則發生溢出,反之則無溢出發生。OV = S + Cy 。以8位二進制爲例,OV = Cy7 + Cy6

       例題4:十六位數舉例。

0100 0110 0101 0010

4652

+  1111 0000 1111 0000

 +   F0F0

  1Cy 0011 0111 0100 0010

 

3742

 

Cy =1   OV = 0

1111 0011 0110 0101

F365

+  1110 0000 0010 0100

 +   E024

  1Cy 1101 0011 1000 1001

 

3742

 

Cy =0   OV = 0

       例題5  8位數舉例。

0111 1000

120

+  0110 0100

  +  100

   1101 1100

 

220

1000 1000

136

+  1001 1100

 +   156

  1Cy 0010 0100

 

292(36)

總結:無符號數  Cy =0 OV = 0

         帶符號數  Cy7 =0  Cy6 = 1  OV = 1

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

所謂符號擴展問題是指一個數從位數較少擴展到位數較多(如從8位擴展到16位,或從16位擴展到32位)時應該注意的問題。

    有符號數是用最高位是0或1來標記正負的,如果最高位是0(如8位數中的第7位,從0位開始算的)表示正數,而是1表示負數。16位數中的第15位控制符號。符號數擴展實稱爲帶符號擴展。只是位數的擴展,不能改變原值的

如0000 1101這個數是帶符號數爲13,擴展爲16位時,一個16位數也要是13的!而這個數是0000 0000 0000 1101就可以了!所以正數的帶符號擴展前邊是加0,這只是一個規律而不是本質,本質就是數大小不改變!

而10001101帶符號數不是-13的!而是將其取補加1就是負數結果,即-0111 0010 + 1,結果就是-115,如果將這個帶符號數擴展時,只有16位1111 1111 1000 1101纔是-115,擴展只是表示範圍大了,而不是改變數值的。如果是正數前8位是0,如果是負數,前8位是1,這樣纔是帶符號擴展的。這不是本質,只是一個規律而已!

    在彙編語言中,我們經常要對字/字節的數據進行操作。當把“字節”轉換成“字”,或“字”轉換成“雙字”時,就需要進行符號擴展符號擴展的具體操作就是把已知信息的最高位擴展到所有更高位。

例1.1 把8位補碼0101 1010、10101100分別擴展成16位補碼。
解:根據符號擴展的含義,“字節→字”的具體擴展結果如下:

 

0101 1010

 

1010 1100

0000 0000

0101 1010

1111 1111

1010 1100

例1.2 把16位補碼0101101111001010、1010111101011011別擴展成32位補碼。
解:根據符號擴展的含義,“字→雙字”的具體擴展結果如下:

 

0101 1011 1100 1010

 

1010 1111 0101 1011

0000 0000 0000 0000

0101 1011 1100 1010

1111 1111 1111 1111

1010 1111 0101 1011

    對於用補碼錶示的數,正數的符號擴展應該在前面補0,而負數的符號擴展則應該在前面補1。例如,機器字長爲8位時,[+46]補=00101110,[-46]補=1101 0010;如果把它們從8位擴展到16位,則[+46]補=00000000 0010 1110=002EH,[-46]補=11111111 1101 0010=FFD2H

 

溢出,符號與進位

轉自:http://www.mouseos.com/arch/Overflow.html

在 rflags 寄存器裏的下面三個標誌位記錄溢出, 符號和進位狀態:

  • OF(Overflow Flag)
  • SF(Sign Flag)
  • CF(Carry Flag)

1. 溢出產生的條件

我們看看下面這個式子,爲了簡單,作爲例子我用 4 位數計算。

1.1 產生溢出

式子1:                                               式子2:
     0 1 1 1             ( 7 )                              0 0 1 1         ( 3 )
+    0 1 1 1             ( 7 )                        +     0 0 1 1         ( 3 )
-------------------------------                       ---------------------------
     1 1 1 0             ( -2 ) 溢出                         0 1 1 0         ( 6 ) 正確

上面的式子中,式子1 在 unsigned 數前提下 7 加 7 結果等於 14,這個結果看似正確。實際上它產生了 Overflow(溢出),結果超出了 4 位數能表達的範圍,這是因爲:當用來表達 signed(符號數)時,結果值 1110 卻是 -2,那麼 7 + 7 = -2 這顯示是錯誤的,因此這裏產生了溢出(超過了表達範圍)。而式子2 是正確的。

在上面兩個式子中,我們可以得到規律:

正數 + 正數 = 負數,就產生了溢出

接着再看下面的式子:

式子1:                                               式子2:
     1 1 0 0             ( -4 )                             1 1 0 0        ( -4 )
+    1 0 0 0             ( -8 )                      +      1 1 1 1        ( -1 )
-------------------------------                      ----------------------------
   1 0 1 0 0             (  4 ) 溢出                       1 1 0 1 1        ( -5 ) 正確
  --                                                      -- 
  進位                                                    進位

再來看看上面兩個式子,式子1 在 singed 數的前提下 (-4) + (-8) = 4,結果很顯然是錯誤的,因此這個式子也產生了溢出。而式子2 中, (-4) + (-1) = (-5) 這是正確的。

同樣我們得到規律是:

負數 + 負數 = 正數,就產生了溢出

1.2 不產生溢出

       1 1 1 1                 ( -1 )
+      0 1 1 1                 ( 7 )
--------------------------------------
    1  0 1 1 0                 ( 6 ) 正確
   ---
   進位

最後再看看這個式子,在 signed 數的情況下:(-1) + 7 = 6 這個結果是正確的,因此這個式子不會產生溢出。

1.3 溢出總結

現在我們可以總結產生溢出的條件:

  1. 兩個正數相加,如果結果爲負數,就產生了溢出。
  2. 兩個負數相加,如果結果爲正數,就產生了溢出。

換個角度來說:

  1. 正數 - 負數 = 負數,就產生了溢出。
  2. 負數 - 正數 = 正數,就產生了溢出。

那麼是什麼情況不會產生溢出的?

  • 不同符號的兩個數相加,不會產生溢出。

那麼我們也可以得出同符號數相減,也不會產生溢出,例如:(-1) - (-1) = -1 + 1 = 0 這個式子是兩個負數相減,等同於負數加正數,這不會產生溢出。

2. 產生進位與借位

上面 5 個式子中,有 3 個產生了進位情況,以 4 位數爲例,當計算的結果高最位向上進一位時,就會產生進位情況,看看其中一個式子:

        1 1 0 0        ( -4 )
 +      1 1 1 1        ( -1 )
 ----------------------------
      1 1 0 1 1        ( -5 ) 正確
     -- 
    進位

計算結果向上進了 1 位,結果依然是正確的,rflags.CF 被置爲 1,rflags.OF 被清 0,如果被作爲 unsigned 數進行計算時:12 + 15 = 27,4 位數表達的結果爲 11,這時需要得到正確結果,需藉助 CF 標誌。在這裏 CF 被置位。

當然這裏還會產生一個借位的情況,下面這個式子:

       0 1 0 1         ( 5 )
-      1 0 0 0         ( 8 )
----------------------------
     1 1 1 0 1         ( -3 )
    --
   借位

兩個數相減,最高位不足減,向前借位,這種情況下也會產生 rflags.CF = 1

3. 符號位

數的最高位描述符號位,以 4 位數爲例,最高位 Bit 3 是符號位,rflags.SF 會根據計算結果置相應的位,rflags.SF = 1 時表示爲負數,而 rflags.SF = 0 則爲正數。

4. 溢出,符號與進位的實際應用

它們較多應用在一些條件判斷的場合,典型地根據兩個數比較結果,做相應的處理。由數存在 unsigned 和 singed 因此在計算機中需要提供 unisgned 數和 singed 數的計算的解決方案。下面我們看看 OF, SFCF 標誌位在 x86/x64 平臺中條件判斷中的應用。

4.1 signed 符號數的比較

首先我們來了解 signed 的條件判斷,下面的代碼:

signed int a;
signed int b;


if (a > b) {
    ... ...

} else if (a < b) {
    ... ...

} else if (a == b) {
   ... ...
}

那麼我可以使用下面的指令來描述上面的 C 代碼:

    mov eax, a
    cmp eax, b

    je equal                ; a == b ?
    jg great                ; a > b ?

    ... ...                 ; No! a < b

    jmp next

great:              
    ... ...                 ; Yes! a > b

    jmp next

equal:
    ... ...                 ; Yes! a == b

next:
    ... ...

在執行 cmp 指令後,processor 會設置 rflags 的標誌位,cmp 指令實際上做減法處理,根據計算結果來更新 rflags 標誌位。

4.1.1 等於

等於情況的判斷比較容易理解,通過判斷 rflags.ZF 標誌,當 rflags.ZF = 1 時,表示 cmp 結果相等。使用指令 jzje 來進行判斷,它們是同一個指令 opcode

4.1.2 大於

我們看看 processor 是如何判斷符號數的大於以及小於的情況,假如有下面兩個比較式子:

  • -1 > -2
  • 4 > -6

前面說過 cmp 指令做減法計算,我們來看看這個過程,同樣以 4 位數作爲例子

式子1:                                              式子2:

-1 - (-2):                                         4 - (-6):

     1 1 1 1    ( -1 )                                    0 1 0 0    ( 4 )
-    1 1 1 0    ( -2 )                               -    1 0 1 0    ( -6 )
-----------------------                             -----------------------
     0 0 0 1    ( 1 )                                     1 0 1 0    ( -6 ) 溢出
                                           

SF = 0                                                SF = 1
OF = 0                                                OF = 1
CF = 0                                                CF = 1
ZF = 0                                                ZF = 0

我們看到對於 -1 > -2 這個比較結果是:

  • SF = 0
  • OF = 0
  • CF = 0
  • ZF = 0

對於 4 > -6 這個比較結果是:

  • SF = 1
  • OF = 1
  • CF = 1
  • ZF = 0

顯然這兩個比較的式子都是 true 的,這個式子中也產生了 carry(也即是借進),它們相同的地方就是 SF = OF,因此我們可以判斷:

SF = OF 時,比較結果是大於。

4.1.3 小於

現在我們來看看小於的情況是如何的?同樣有下面的兩個比較式子:

  • -1 > 2
  • -3 > 6

這兩個式子我們知道,它們都是 false 的,同樣我們來看看如何比較:

式子1:                                              式子2:

-1 - 2:                                             -3 - 6:

     1 1 1 1    ( -1 )                                    1 1 0 1    ( -3 )
-    0 0 1 0    ( 2 )                                -    0 1 1 0    ( 6 )
-----------------------                             -----------------------
    1 1 0 1    ( -3 )                                    0 1 1 1    ( 7 ) 溢出
                                                   
                                                  
SF = 1                                                SF = 0
OF = 0                                                OF = 1
CF = 0                                                CF = 0
ZF = 0                                                ZF = 0

式子 -1 > 2 的比較結果是:

  • SF = 1
  • OF = 0
  • CF = 0
  • ZF = 0

式子 -3 > 6 的比較結果是:

  • SF = 0
  • OF = 1
  • CF = 0
  • ZF = 0

這兩個式子都是 false 的,它們相同之處就是 SF <> OF 我們這裏得出來的結果是:

SF <> OF 時,比較結果是小於

4.1.4 signed 數的條件跳轉指令

x86 提供了一系列的條件跳轉指令來處理條件判斷,下面是基本 signed 符號數的條件判斷指令:

  • jg 大於
  • jge 大於或等於
  • jl 小於
  • jle 小於或等於

上面這 4 條是基本 singed 數判斷指令,然而根據不同的表達詞,又可以變化出多種助記符,例如:

  • jg 可以寫成 jnle(不小於等於)
  • jge 可以寫成 jnl(不小於)
  • jl 可以寫成 jnge(不大於等於)
  • jle 可以寫成 jng(不大於)

加上了一些否定修飾產生同樣效果的指令,實際上這些對應的否定詞指令是同一個 opcode 的不同表現形式,使用哪個,依據個人喜好。

這些指令正是使用了 rflags 寄存器的 SF, OF 以及 ZF 進行條件判斷,例如:

  • jg 指令:當 ZF = 0 並且 SF = OF 時就跳轉
  • jl 指令:當 SF <> OF 時就跳轉,這裏不需要判斷(ZF = 0),僅需判斷 SF <> OF 條件就足夠了,因爲:當 ZF = 1 時,必然 SF = OF
  • jge 指令:當 SF = OF 時就跳轉,這裏不需要判斷(ZF = 1),僅需判斷 SF = OF 條件就足夠了(因爲:無論 ZF 是否爲 1 都滿足大於或等於的條件
  • jle 指令:當 ZF = 1 或者 SF <> OF 時就跳轉轉,結果爲 0 也滿足條件(也就是當 ZF = 1(此時 SF = OF) 或者 SF <> OF 的情況下,都滿足小於或等於的條件)

4.2 unsigned 無符號數的比較

對於無符號數來說,判斷大小則簡單多了,我們將上面的 C 代碼改一改,a 和 b 定義爲 unsigned 無符號:

unsigned int a;
unsigned int b;

if (a > b) {
    ... ...

} else if (a < b) {
   ... ...  
 
} else if (a == b) {
   ... ...
}

同樣我可以用以下彙編指令描述爲:

    mov eax, a
    cmp eax, b

    je equal              ; a == b ?
    ja above              ; a > b ?

below:
    ... ...               ; No! a < b
  
    jmp next

above:
    ... ...               ; Yes! a > b

    jmp next

equal:
    ... ...               ; Yes! a == b

next :
    ... ...

結構上是和 singed 數是一致的,只是使用了不同的指令。

4.2.1 大於

我們來看看 unsigned 數的大於情況,下面兩個比較式子:

  • 12 > 3
  • 12 > 6

式子1:                                     式子2:

12 - 3:                                     12 - 6:

       1 1 0 0        ( 12 )                         1 1 0 0      ( 12 )
-      0 0 1 1        ( 3 )                 -        0 1 1 0      ( 6 )
----------------------------                ----------------------------
       1 0 0 1        ( 9 )                          0 1 1 0      ( 6 )

SF = 1                                      SF = 0
OF = 0                                      OF = 1
CF = 0                                      CF = 0
ZF = 0                                      ZF = 0

顯然我們知道這兩個式子都是 true 的,從上面的計算式子我們可以得到,對於 unsigned 數的比較:

當 CF = 0 時,比較結果大於

當 CF = 0 時表明計算結果沒有產生借位,因此比較結果是大於。另外我們可以看出,當這個式子是 signe 符號數時,結果是小於。

4.2.2 小於

下面兩個條件判斷式子:

  • 7 > 8
  • 5 > 7

式子1:                                     式子2:

7 - 8 :                                     5 - 7:

       0 1 1 1        ( 7 )                         0 1 0 1      ( 5 )
-      1 0 0 0        ( 8 )                 -       0 1 1 1      ( 7 )
----------------------------                ----------------------------
       1 1 1 1        ( 15 )                        1 1 1 0      ( 14 )

SF = 1                                      SF = 1
OF = 1                                      OF = 0
CF = 1                                      CF = 1
ZF = 0                                      ZF = 0

由上面的計算式子可以看到:

當 CF = 1 時,比較結果是小於

同樣當 CF = 1 時,表示被減數不足,需要借進,因此它是小於的。

4.2.3 unsigned 數的條件跳轉指令

x86 提供了 4 個基本的 unsigned 數條件跳轉指令:

  • ja 高於
  • jae 高於等於
  • jb 低於
  • jbe 低於等於

同樣也可能加上否定修飾,變成以下幾種形式:

  • ja 也可以爲 jnbe(不低於等於)
  • jae 也可以爲 jnb(不低於)
  • jb 也可以爲 jnae (不高於等於)
  • jbe 也可以爲 jna(不高於)

它們是依據 CFZF 進行判斷:

  • ja 指令:當 CF = 0 且 ZF = 0 時跳轉
  • jb 指令:當 CF = 1 且 ZF = 0 時跳轉
  • jae 指令:當 ZF = 1 或者 CF = 0 時跳轉
  • jbe 指令:當 ZF = 1 或者 CF = 1 時跳轉

jb 指令的另一個形式可以爲 jc(當 CF = 1 時跳轉)。

5. eflags 標誌位的設置

x86/x64 平臺裏的加減類運算中,指令會根據 unsigned 和 singed 結果設置 eflags 標誌位,下面是 Intel 手冊的一段話:

The SUB instruction performs integer subtraction. It evaluates the result for both signed and unsigned integer operands and sets the OF and CF flags to indicate an overflow in the signed or unsigned result, respectively. The SF flag indicates the sign of the signed result.

指令會對結果進行一個評估,評估根據操作數的 unsigned 和 signed 兩方面。

那麼,我們可以做出下面的推斷,還是以 4 位數減法做爲一個例子:

        0 1 1 1      ( 7 )
-       1 0 0 0      ( 8 )
--------------------------
        1 1 1 1      ( 15 )

這個二進制減法的結果的 1111B,sub 指令對這個結果進行評估:

  • 對於 unsigned operand 來說:eflags.CF = 1
  • 對於 singed operand 來說:正數減負數,結果爲負數,結果溢出了,eflags.OF = 1efalgs.SF = 1

條件跳轉指令可以對計算指令區分 unsigned 和 singed 兩種情況:

mov eax, 0x70000000
mov ebx, 0x80000000

sub eax, ebx                                 ; 0x70000000 - 0x80000000

在例子中的 0x70000000 - 0x80000000 的減法計算中:

  • 對於 singed 計算結果,設置 eflags.OF = 1 和 eflags.SF = 1
  • 對於 unsinged 計算結果,設置 eflags.CF = 1

那麼,我們可以使用兩種條件跳轉指令:

sub eax, ebx

jg .L1                                       ; signed 跳轉指令

ja .L2                                       ; unsigned 跳轉指令

 

 

發佈了14 篇原創文章 · 獲贊 13 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章