Linux | c&cpp | Email | github | QQ群:425043908 关注本站

itarticle.cc

您现在的位置是:网站首页 -> 代码相关 文章内容

c++算术和位运算中类型转换和提升规则-itarticl.cc-IT技术类文章记录&分享

发布时间: 8年前代码相关 124人已围观返回

#include <iostream>


using namespace std;


void main()

{

/************************************************************************/

/* 结论:

Memory中存储的是补码,是根据存入的具体数据的值进行转换为二进制

后的形式,如果超过该数据类型范围则不知道存的形式是什么了,这个

应该是编译器相关的,对越界数的处理方式;(无论存入的时候是用什

么进制表示的,在这里注意+/-单目运算符的作用)


无论要做什么运算都要先看这个数存入内存是什么样子的;


类型提升/截断的时候都是对内存中数据的操作,提升的时候就是需要

进行扩位:对无符号数扩位的时候直接把扩出的位上补零即可(因为unsigned都是正数);有符号

数进行扩位,则进行符号位扩展(sign extension)如果是整数即原最高位

是零则补零,如果是负数即原最高位是1则全补1;进行数据截断的时候是

从低位开始数位数然后截断,这里注意我们使用的一般都是小端机(little Endian)


类型强制转换其实也是对内存中存储的数据的一种呈现方式而已,如:内存

中是8000的时候(即1000 0000 0000 0000),如果用unsigned short int则打印出

来的是2的15次方,如果用short int则打印出来是负2的16次方


在进行算术运算和双目的位运算的时候如果操作数数据类型不一致,首先进

行数据 Arithmetical Conversion类型提升(其实提升就是进行扩位,然后把扩位后的内存二进制代码强制转为某种数据类型之后进行打印),然后 才开始运算


在进行单目位运算的时候,首先进行整型提升,然后开始运算Integer Promotion*/

/************************************************************************/

//目的:计算机内部存储形式(无符号数、有符号正数、有符号负数、直接十六进制;及其

//扩位后规则及其扩位后的存储形式)

unsigned char uch1 = 0x80; //80

unsigned char uch2 = 128; //80


//注意+/-作为单目运算符的作用和意义,就是让存入内存的时候,最高位写为1

//同时要注意各个数据类型的表示范围

char sch1 = -0x7f; //Memory中应该是81

char sch2 = 0x80;

cout << (short int)sch1 << endl;//-127

cout << (short int)sch2 << endl;//-128


unsigned usch3 = 0x7f;

unsigned ushc4 = 0x80;

cout << (short int)usch3 << endl;//127

cout << (short int)ushc4 << endl;//128


char sch3 = 127;

char sch4 = -128;

cout << (short int)sch3 << endl;//127

cout << (short int)sch4 << endl;//-128


unsigned uch3 = 0;

unsigned uch4 = 255;

cout << (short int)uch3 << endl;//0

cout << (short int)uch4 << endl;//255


char a = -0x80;

short int b = 0x7f80;

short int result = a&b;//0111 1111 1000 0000


cout << result <<endl;


}

二、负数的补码

在计算机系统中,数值一律用补码来表示(存储)。

主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。

补码与原码的转换过程几乎是相同的。

数值的补码表示也分两种情况:

(1)正数的补码:与原码相同。

例如,+9的补码是00001001。

(2)负数的补码:符号位为1,其余位为该数绝对值的原码按位取反;然后整个数加1。

例如,-7的补码:因为是负数,则符号位为“1”,整个为10000111;其余7位为-7的绝对值+7的原码0000111按位取反为1111000;再加1,所以-7的补码是11111001。

已知一个数的补码,求原码的操作分两种情况:

(1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。

(2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1。

例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为“1”;其余7位1111001取反后为0000110;再加1,所以是10000111。


三、原码补码反码

一般来说,初学二进制编码时,会看到如下描述:

原码表示法是机器数的一种简单的表示法。其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示。

机器数的反码可由原码得到。如果机器数是正数,则该机器数的反码与原码一样;如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。

机器数的补码可由原码得到。如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。

如果是为了考试,死记即可。但我总想搞清楚为什么计算机里面的数要这样子表达?意义何在?-128的补码为什么是10000000?为什么补码有这么奇怪的运算规则?计算机算减法的时候都需要从源码到补码的计算吗?

思路

google了一下,看到了这样一篇文章,注意到文中关于补码来历的描述,可以总结如下:

计算机里面,只有加法器,没有减法器,所有的减法运算,都必须用加法进行。

用补数代替原数,可把减法转变为加法。出现的进位就是模,此时的进位,就应该忽略不计。

二进制下,有多少位数参加运算,模就是在 1 的后面加上多少个 0。

补码就是按照这个要求来定义的:正数不变,负数即用模减去绝对值。

补充解释一下“模”的概念(不准确):

考虑时钟上时间的计算,假设现在时针指向数字3,若问“6小时前时针指向的数字是几”,则可以:

1. 将时针逆时针拨动6格。

2. 将时针顺时针拨动12 - 6 = 6格。

两者的结果是一样的。这里称12为“模”。

故有 3时 - 6个小时 = 3时 + (12 - 6个小时),这里可以看到将减法转换成加法的过程,即“加上模减去绝对值的差”。

所以,假设模是10,有效位数为1,当我们计算 9 - 7 的时候:

9 - 7 => 9 + (10 - 7) = 12,去掉最高的位后,得到2,这是正确的结果。

作者的意思是说,计算机里面所有数都以补码形式保存,加减运算都是补码之间的加法运算。然后作者提出了一个我之前没听过的观点:

补数 和 补码的定义式 里面,根本就没有什么符号位。这最高位的1、0是自然出现的,并不是由人来规定的。

的确,符号位在补码运算里面是“模”,本身并不带符号的意义。因为计算机将加法转换成加上一个“负数”,而负数又以补码的形式表现。补码比源码多一位,从这多出来的一位可以推断出原来数字的正负号,所以成为了符号位。也可以这样认为,留出一位(不全部占满)的原因是要用“模”来表示正负数。

也就是说,不是特意留出一个符号位,用1和0来表示正负号。而是补码运算可以用最高位来表示正负,所以符号位诞生了。

那么为什么-128的补码是10000000?可以这样理解。-128是一个负数,所以它的补码是它的“模”减去它的绝对值,即:

100000000 - 10000000 = 10000000

那么为什么负数补码等于源码的反码加一呢?可以这样推导:

100000000 - 10000000

= (11111111 + 00000001) - 10000000

= 11111111 - 10000000 + 1

= 01111111 + 1 //反码加一

= 10000000

由此我们得知,在计算机里面所有的数字都以补码形式存储。127存成01111111,-127存成11111111,算减法就变成算加法了,尽管你看到的是“-”号。

发布时间: 8年前代码相关124人已围观返回回到顶端

很赞哦! (1)

文章评论

  • 请先说点什么
    热门评论
    123人参与,0条评论

站点信息

  • 建站时间:2016-04-01
  • 文章统计:728条
  • 文章评论:82条
  • QQ群二维码:扫描二维码,互相交流