汇编语言符号怎么记 如何将无符号数转化为有符号数

关于有符号数和无符号数的转换
今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题,被它们之间的转换原理和位移原理搞得头大了。真的很后悔本科的时候没有认真学习《计算机组成原理》/《计算机操作》等计算机基础课程。以下是我根据相关知识回顾和整理的材料,如有和某某的文章有雷同之处,请勿见怪。另外也希望看到这篇文章的同志们能够有所收获吧。
#include &cstdio&
#include &iostream&
int main()
& & ui = (unsigned short int)0x8000u;
& & si = (signed short int)0x8000;
& & printf(&ui = %u\n&,ui);
& & printf(&si = %d\n&,si);
& & ui = ui&&1;
& & si = si&&1;
& & printf(&ui = %u\n&,ui);
& & printf(&si = %d\n&,si);
& & cout&&&------------------------------&&&
& & ui = (unsigned short int)0x8000u;
& & si = (signed short int)0x8000;
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & ui = ((signed short int)ui&&1);
& & si = ((unsigned short int)si&&1);
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & cout&&&------------------------------&&&
& & ui = (unsigned short int)0x8000u;
& & si = (signed short int)0x8000;
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & ui = ui&&1;
& & si = si&&1;
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & cout&&&-------------------------------&&&
& & ui = (unsigned short int)0x8000u;
& & si = (signed short int)0x8000;
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & ui = ((signed short int)ui&&1);
& & si =((unsigned short int)si&&1);
& & printf(&%u\n&,ui);
& & printf(&%d\n&,si);
& & return 0;
显示结果:
ui = 32768
si = -32768
ui = 16384
si = -16384
------------------------------
------------------------------
-------------------------------
&在计算机中,可以区分正负类型的数,成为&有符号数&(signed);无正负类型的数(只有整数类型),成为&无符号数&(unsigned)。简明的说,无符号说就是其所有的位数都用来表示数值的大小,有符号数除最高位来表示数值的正负外(0表示正数;1表示负数),其余各位用来表示数值的大小。举个例子说明一下:十机制数 正数255 &二进制表达形式:
十机制数 负数-1 & & 二进制表达形式:&
可见-1的二进制的最高位为红色的1,可是为什么其表达形式为而不是呢?这就关于任何数在计算机内是以补码形式存储问题。下面会介绍的,在此先不详细说明了。
3.存储范围
从前面的介绍可以知道,由于有符号数的最高位被拿来用作符号位,所以它所能够表达的最大数值要小于无符号数所能够表达的最大数值。还是举个例子来说明一下吧:
无符号数: 十进制值:255
有符号数: 十进制值:127
这是有人可能会提出这样的疑问:有符号数所能够表达的数值范围会不会小于无符号数所能够表达的数值范围呢?
呵呵,答案是否定的!虽然有符号数在表达最大值上的能力减弱了,但是它能够表达负数。负数的个数可以弥补其不足。来让我们比较一下:
一个字节的无符号数的表达数值范围是:[0,255]
一个字节的有符号数的表达数值范围是:[-128,0),[0,127]
可见它们都能够表示256个数。
4.各种码(原码/反码/补码)
有些人也许会这样认为&-1&(双字节)在计算机中的表达形式为00 0001,可是实际上不是的。计算机是以其补码的形式进行表达的,即&-1&(双字节)的表达形式是11 1111。
说一下各种码的概念吧。
原码:一个整数,按照绝对值的大小转换成二进制数,最高位为符号位。
反码:将原码除最高位(符号位)外,其余各位按位取反,所得到的二进制码。正数的反码为原码。
补码:反码最低位加1即为补码。
关于负数的补码求法说明一下,先得到其反码,之后将反码加1即可。有些大神根据其原码,闭眼即得,这种能力需要修炼一下啊。
这时有些人可能会说,为什么要引入补码的形式呢?直接按照原码存储不就省事很多吗?嘿嘿,要记住,有些事情并不是你想省事就能省事的。好了来欣赏一下补码的优势吧。
计算机的带符号数用补码表示的优点:
1负数的补码与对应正数的补码之间的转换可以用同一种方法-求补运算完成,可以简化硬件。
2 可将减法变为加法,这样减法就可以用加法器进行计算了。
3 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
心算求补(大神求补算法):
从最低位开始至找到的第一个1均不变,符号位不变,这之间的各位&求反&(0变1;1变0)。
原码: &补码:.
5.有符号数与无符号数的相互转换
无符号整数和有符号整数之间进行强制类型转换时,位模式不改变。
有符号数转换为无符号数时,负数转换为大的正数,相当于在原值上加上2的n次方,而正数保持不变。
无符号数转换为有符号数时,对于小的数将保持原值,对于大的数将转换为负数,相当于原值减去2的n次方。
当表达式中存在有符号数和无符号数类型时,所有的操作都自动转换为无符号类型。可见无符号数的运算优先级高于有符号数。
unsigned int a = 20;
signed int b = -130;
&运算一下结果是 b&a 。
无符号int与有符号int比较大小,转化为无符号int来比较
int类型与非无符号int的类型比较时,非无符号int的类型转化为int来比较
无符号int类型与其他类型如unsigned short,signed short,unsigned char, char 比较时,其他类型一律转化为无符号int类型来比较
非无符号int类型和非int类型如unsigned short,signed short,unsigned char, char 比较时,一律转化为int类型来比较
6.转换大餐
有符号数的转换
原类型 目标类型 转换方法
符号位扩展
char long 符号位扩展
char unsigned char 最高符号位失去位意义,变为数据位
char unsigned short 符号位扩展到short;然后从short转到unsigned short
char unsigned long 符号位扩展到long;然后从long转换到unsigned long
char float 符号位扩展到long;然后从long转到float
char double 符号位扩展到long;然后从long转换到double
char long double 符号位扩展到long;然后从long转换到long double
short char 保留低位字节
short long 符号位扩展
short unsigned char 保留低位字节
short unsigned short
最高为失去意义,变为数据位
short unsigned long 符号位扩展到long;然后从long转到unsigned long
short float 符号位扩展到long;然后从long转到float
short double 符号位扩展到long;然后从long转到double
short long double 符号位扩展到long;然后从long转换到long double
long char 保留低位字节
long short 保留低位字节
long unsigned char 保留低位字节
long unsigned short 保留低位字节
long unsigned long 最高为失去意义,变为数据位
long float 使用单精度浮点数表示,可能失去精度
long double 使用单精度浮点数表示,可能失去精度
long long double 使用单精度浮点数表示,可能失去精度
无符号数的转换
原类型 目标类型 转换方法
unsigned char char 最高为作符号位
unsigned char short 0扩展
unsigned char long 0扩展
unsigned char unsigned short 0扩展
unsigned char unsigned long 0扩展
unsigned char float 转换到long;然后从long转换到float
unsigned char double 转换到long;然后从long转换到double
unsigned char long double 转换到long;然后从long转换到long double
unsigned short char 保留低位字节
unsigned short short 最高为作符号位
unsigned short long 0扩展
unsigned short unsigned char 保留低位字节
unsigned short unsigned long 0扩展
unsigned short float 转换到long;然后从long转换到float
unsigned short double 转换到long;然后从long转换到double
unsigned long long double 转换到long;然后从long转换到long double
unsigned long char 保留低位字节
unsigned long short 保留低位字节
unsigned long long 最高位作符号位
unsigned long unsigned char 保留低位字节
unsigned long unsigned short 保留低位字节
unsigned long float 转换到long;然后从long转换到float
unsigned long double 直接转换到double
unsigned long long double 转换到long;然后从long转换到long double
7.各种数据类型所占字节一.有符号数的加减法
1、符号数与无符号数的人为规定性:
一个数,是有符号数还是无符号数都是人为规定的。进行二进制运算时用无符号数或是补码运算时,结果都是正确的。
若规定为无符号数,即 132+146=146D 。
若规定为符号数,则为-124+14=-110,而[-110]补=。解释:是 -124的补码,的补码,在机器中运算后得出的结果是[-110]的补码。机器中的有符号数的运算一般就是补码的运算。
2、补码加减法运算
&&计算机中,当确定为符号数运算时,符号数一律用补码表示,运算时符号位和数字位一起参加运算。同样,运算结果也用补码表示。
1)两符号数相加公式:
[X+Y]补 (MOD2)=[X]补+[Y]补 & &//MOD2即 mod 2^n,即丢弃符号位的进位(因为符号位参与运算是补码运算的特点)
2)两符号数相减公式:
[X-Y]补 (MOD2)= [X]补+[-Y]补
求3CH-90H。
首先3CH=,90H=
(1)当为有符号数时,显然这两个数是某两个数的补码。(具体是哪两个数X Y, 需要自己计算)。运算结果也是补码,是X-Y的补码 [X-Y]补。机器只运算到这一步。[X-Y]补 就是运算结果,溢出 进位等标志位也都是这个补码运算过程的结果,由硬件ALU运算。X-Y并不是机器的运算结果,由[X-Y]补求X-Y是在计算机组成原理中由程序员或者编译器完成的工作,对于编译器来说运算结果才是X-Y,编译器属于软件,不属于硬件机器。
根据微机中有符号数(补码)的减法公式,[x]补(输入)-[Y]补(输入)=[x]补+[-Y]补(求补操作结果)=[X-Y]补(微机运算结果) ,求[-Y]补是关键。
[x]补=,[Y]补=,
[-Y]补=[Y]补的 求补=(取反)+1(加一)= &//不是[Y]补的补码!!更不存在什么 -[Y]补 的补码(不存在-[Y]补这种写法)
&解释求补操作: &
无论-[Y]补的符号位是0还是1。正数也可以有求补操作,只是得到的数不叫补码,就是为了把减法转换成加法。求补操作 就是取余(求补)(补码是人规定的,但是求补是数学定义)。证明[-Y]补=对-[Y]补 求补操作:[0]补=[-x]补+[X]补 。[-x]补和[X]补不是正数和负数的关系!!而是互补关系,算数相反,不是逻辑相反。另外仔细想一下,我们学补码是为了什么?以前是为了求一个原码的补码是什么,因为计算机组成原理中 从编译器到微机 从微机到编译器 需要原码变成补码 补码变成原码,但是在微机原理中,就不需要这一部分了,而是需要 求补操作了。计算机组成原理中,是由y原码求出来的[-y]补,而微机原理中是 对[y]补 求反操作出来的[-y]补,计算过程都不一样。计算机组成原理中的过程可以理解 但是不是微机真正的运算过程,这一次学习的微机原理的补码的加减法才是真的硬件的运算过程!
另外,补充一点,X=[X]补 的补码,由补码求原码时不用按照由原码求补码的逆过程(补码为时例外!)。
《微机原理与接口技术》中写道:“由加法器的原理图,加法器的方式控制M用于控制加减法,当M=0时进行S=A+B操作,当M=1时进行S=A-B操作。当M=1(减法)时,各个异或门对B的各位进行求反,并将1作为初试进位加入结果,也就是执行对B的求反加1,即求补操作。” 也就是说!无论B是有符号数还是无符号数,无论符号位是0还是1,只要M=1,就统统对B执行求补操作!!!所以,求补操作可以对符号位是0的数执行!
所以,[X-Y]补=01 00+=ACH
(2)补码最大好处就是不管是有符号数还是无符号数都可以用同一套加减法。系统对有符号数和无符号数的加减法都采用一样的策略。
无符号加减法不存在溢出问题,只是将进位或借位存储在CF中。机器不知道你进行的运算是否有符号,如果你进行的是有符号运算,你需要查看OF,否则不需要。所以,溢出不溢出,是由程序员判断的,机器不知道。
不管是有符号数还是无符号数,微机都是按补码运算,至于是进位还是溢出不仅要看进位标志位,还要看溢出标志位。
只不过在做无符号数运算时程序员不考虑溢出标志位,只考虑进位标志位而已。比如,你说它是无符号数7+13呢,还是有符号数7-3呢?对微机而言这是一回事!
所以,微机中,无符号数时和有符号数时的运算结果一定是一样的。
(3)注意这道题和《计算机组成原理》学补码加减运算时的题目的区别。
例子:X=+0101,Y=-1010,求X-Y。
题目给的X Y,是原码, 所以已经默认是有符号数,也有判断是否溢出,所以默认的计算过程也是指机器运算过程。
原码在机器中 均是以补码保存和运算的,
[x]补-[Y]补=[x]补+[-Y]补=[X-Y]补,只用到了[x]补+[-Y]补=[X-Y]补
属于类型:已知X Y,所以先求[X]补, [-Y]补 ,便求得[X-Y]补。然后求X-Y。
也就是说,上一题已知的是[X]补 [Y]补,(机器的)运算结果是[X-Y]补。中间过程没必要求出X Y是什么数。
本题已知的是X Y,先得出机器的运算结果[X-Y]补,在做编译器的工作由[X-Y]补 求出X-Y。
二者求[-Y]补的方法不一样:前者是对[Y]补 求补操作 求得的,后者是由-Y求得。
可以说,后面例子是编译器和微机共同工作的过程,前面例子只关系微机运算。
&----------------------------------------------------------------------------------------
二.那么系统是怎么识别有符号数和无符号数的呢?
(1)CPU只会根据输入信号进行逻辑运算,在硬件级别是没有有符号无符号的概念,运算结束会根据运算前的信号和输出信号来设置一些标志位,是不是有符号由写程序的人决定,标志位要看你把操作数当有符号还是无符号来选择,就像内存中的数据,你可以按照需要来解析,原始数据在那里,你要按什么数据格式来解析在于自 己的选择;
在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!;
计算机对有符号整数的表示只 采取一套编码方式,不存在正数用原码,负数用补码这用两套编码之说,大多数计算机内部的有符号整数都是用补码,就是说无论正负,这个计算机内部只用补码来 编码!!!只不过正数和0的补码跟他原码在形式上相同,负数的补码在形式上与其绝对值的原码取反加一相同。
有符号数和无符号数在计算机里表示都是一样的,二进制的补码形式。是有符号还是无符号,是编译器来辨认的。例如:unsigned char uch, char ch;在内存中有个数0b.把它赋给uch,那么uch就是127如果赋给ch,那么ch就是-1
---------------------------
《微机原理与接口技术》中写道:“在微机中,对于定点数存在无符号数和有符号数的概念。无符号数表达的是 大于等于0的数,在机器表示时不需要表示符号的信息。...。在计算机中,常用8位,16位,32位,64位等字节整数倍的位数进行定点数的表示。”也就是说,《计算机组成原理》中的5位 7位等的数,实际上是没有意义的,当时只是为了理解一些运算概念才出的那些题。
《微机原理与接口技术》中写道&:“时钟一圈是12个小时,顺时针+1,逆时-1,这样,在时钟上-1和+11是一样的。类似,用8位来表示有符号数,它的一周是2^8=256,所以-1就可以用255(2^8-1)来表示,这就是补码的含义。
再次强调:微机中有符号数的机器表示是补码。在微机中,将“求反加1”操作称为求补操作,又称为算数求反(在数的前边加上负号)。补码具有以下特性:
& & & & & & & & & & & & & & & & & & & & & & & & [-x]补=[x]补的求补 & & //所以,[-x]补 并不是 -[x]补 的补码!!//在前面加上负号 就是要求补操作:按位求反加一。
                 & [x+y]补=[x]补+[y]补
& & & & & & & & & & & & & & & & & & & & & & & & [x-y]补=[x]补+[-y]补&
有第三个补码特性可以看出,补码的减法运算可以变为加法运算。这样,cpu中就可以用加法器直接实现减法,而不再需要再专门设置实现补码减法的部件了。”
也就是说,微机原理中的加减运算都是补码加减运算,和计算机组成原理中的加减运算不一样了!因为是计算机组成原理,所以包括了微机补码加减运算和编译器运两个工作部分算,而微机原理只有补码加减运算部分,只是计算机组成原理的运算过程中的一部分了。所以,一定要注意计算机组成原理和微机原理的区别,前者包含后者。
阅读(...) 评论()博客访问: 3914773
博文数量: 622
博客积分: 1548
博客等级: 上尉
技术积分: 16327
注册时间:
认证徽章:
http://www.csdn.net/
https://www.kernel.org/
http://www.51develop.net/
http://www.
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 嵌入式
转载自:/asmsky/blog/item/cab6da277fb5b8.html
一、只有一个标准!
在汇编语言层面,声明变量的时候,没有 signed & 和 & unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是正确的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)
二、存在两套指令!
第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
举例来说:
内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 。(参看文后附录2例程)
三、可爱又可怕的c语言。
为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl 也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)
C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,java之类就太远了)。当初K&R 版的c就是高级一点的汇编……:-)
C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:
int main()
int x = 2;
char * str = "abcd";
int y = (x - strlen(str) ) / 2;
printf("%d\n",y);
结果应该是 -1 但是却得到: 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
观察编译后的代码,除法指令为 div ,意味无符号除法。
解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用 无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)
为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)
1:IA-32 Intel Architecture Software Developer’s Manual》第2卷PDF文档表述为“sign-extended imm8”,这个sign-extended 是符号扩展的意思。说说符号扩展:当操作数进行长度扩展时,既要让操作数变长又不能改变原数值,所以就出现了符号扩展一说。比如 movsx ax, 0xEC & ,执行扩展后,ax的值为:0xFFEC,长度变长了,结果没变,都是 -20 。
2:两套乘法指令结果例程
;; 程序存储为 x.s
;;--start--------------------------------------------------------
extern printf
global main
section .data
str1: db "%x",0x0d,0x0a,0
n: db 0x02
section .text
xor eax,eax
mov al, 0xec
mul & byte [n] & ;有符号乘法指令为: imul
call printf
add esp,byte 4
阅读(3637) | 评论(0) | 转发(1) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。有符号数和无符号数之间的转换
{&unsigned a=32768;
&& printf(“a=%d\n”,a); }
答案是 a=-32768。
首先,unsigned是表示一个变量(或常数)是无符号类型。signed表示有符号。
unsigned&& signed
 无符号数的数值范围0-65535,有符号数的数值范围在-32768-
+32767之间.所以有符号运算比无符号运算耗资源.
& 无符号数32768的二进制形式为00 0000.
& 在转换为有符号数时,由于最高位表示符号数,因此可以理解其为一个负数。负数采用2的补码(two's
complement)表示。“要把内存中以补码形式存放的二进制码转换成十进制的负数步骤如下:
& 1.先对各位取反
& 2.将所得的二进制数转换成十进制数
& 3.因为最高位为1,因此在所得的十进制数前面加负号.
& 4.对所求得的数再减1,即可.
& 按照上面的步骤:各位先取反,11 1111
;再转为十进制数,32767;再在所得的数前加负号,-32767;再减1,即为-768.。
unsigned&& signed
{ int& b=-2;
& printf(“b=%u”,b); }
答案是b=65534。
其具体步骤为:
&&OE先取-2的绝对值2的原码:
0000&0010&
&将其转换为反码:11
&Ž加1补码:
十进制形式就是无符号数 65534
这就是我理解后的复述.欢迎大家一起讨论
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。安全检查中...
请打开浏览器的javascript,然后刷新浏览器
< 浏览器安全检查中...
还剩 5 秒&}

我要回帖

更多关于 将汇编语言编写的程序 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信