溢出与进位

溢出与进位

于无符号数来说,不存在溢出的问题,它的进位就相当于有符号数中的溢出
而对有符号数来说,不存在进位的问题

一个字节(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 跳转指令 

转载自:https://blog.csdn.net/rjs123/article/details/7709325


猜你喜欢

转载自blog.csdn.net/weixin_41573650/article/details/80727556