算法分析与设计(第二周)【已完结】

本周知识要点:

2.1. C语言中对整数的存储机制了解

2.2. 进制转换(二进制转十进制,十进制转二进制)

2.3. 应用C语言中存储机制 做一个bit算法,这对某些计算将会特别快

 

1、整数的一些基础知识要点:(参考C语言中整数的存储形式

C语言中整形按大小可以细分int、short、long,又可以按符号分有符号和无符号等,又因C标准并未明确规定不同数据类型的标准大小,而交由编译器规定。故在不同系统、不同编译器下大小不同,从存储来说,最大的不同即在于有无正负号,为了描述方便,便于理解,我们统一按照最常见的四字节为例(2字节和8字节也通用),按有符号整数和无符号整数

1.1 无符号整数(unsigned int)

储时,它们(unsigned int)的全部位数(比如int四个字节,将占8*4=32个bit位)都用来表示数据,即00000000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111 之间,而在内存中,通常用8个十六进制数表示,即四个二进制数一组,十六进制表示为0x00000000 ~ 0xFFFFFFFF。最大表示范围十进制为0~4294967295。

 

1.2 有符号整数(int)

有符号数与无符号数的区别在于,最高一位也就是左边第一位是用来表示符号的,而右边31位表示数据。
左边第一位用1表示负数,用0表示正数
右边31位数据即为0000000 00000000 00000000 00000000 ~ 1111111 11111111 11111111 11111111
对应十六进制数为0x00000000 ~ 0xFFFFFFFF ,其中正数部分为:0x00000000 ~ 0x7FFFFFFF,负数部分为:0x80000000 ~0xFFFFFFFF,其中0x00000000为0,0x80000000为最小负数。
对应十进制数则为-2147483648 ~ 2147483647

 

1.3 十进制在计算机二进制存储规则(参考 int类型在内存中的存储方式

① 占用的比特位数量

在32位操作系统下,两者都是占用4个字节,每个字节有8个比特位,因此有32个0-1的二进制位数。两者的不同在于,int类型有正负号(±)的存在,需要比unsigned int类型多消耗一个位数。

② 符号的表示方法

在所有被int类型占用的比特位中,左起第一个位就是符号位。int类型的符号位上,0表示正数,1表示负数。在32位操作系统下,其余后面31位是数值位。

③ 数字0的表示方法

按照上面提到的符号,我们有了两种0的表示方法,即“+0”和“-0”。

      实际上,在32位系统下int类型中,我们计算机已经强行规定了这种情况,数字0采用“+0”的表示方法,即00000000 00000000 00000000;而“-0”这个特殊的数字被定义为了-2^31。

      因此我们看到32位系统下int类型的取值范围中,负数部分比正数部分多了一个数字,正数的最大取值是2^31-1,而负数的最小取值是-2^31。正数部分之所以要减去1,是因为被数字0占用了,而负数部分不需要用来表示0,因此原本的“-0”就用来表示-2^31这个数字。

(所以 这个能很好的解决+0 和-0 的区别,而只唯一剩下一个0)

而且,还有一个规定,当十进制对应的二进制全是1时, 即11111111 11111111 11111111 (最大的负整数,实际是有符号的-1)这时候再+1就会变成000000000 00000000 00000000(最小的非负整数),而01111111 11111111 11111111(最大的非负整数)+1,就会变成10000000 00000000 00000000(最小的负整数)这样就会形成一个“整数圈”,使得任意的加减法都会在这个圈子里运算。

例子:-1+1 = 0,以及,2147483647+1 = -2147483648。

为什么会出现这种情况,原因主要还是便于人类理解加减法,更加详细的请看下面。

1.4 原码、反码和补码的概念

① 原码的概念

原码:是计算机中一种对数字的二进制定点表示方法。原码表示法在数值前面前面有一位符号位(即最高位为符号位),正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。

实际上:(以八位为例)

十进制的 1 对应二进制就是 00000001

十进制的 -1 是否就是对应二进制的 10000001呢?答案是否定的,因为10000001 如果+1 就变成10000010,就变成-2了,不符合人类对加减法的认知,所以不应该这样编码。

② 反码的概念

反码表示法的规定:“正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。”

即:

对于正数和“+0”而言,其原码本身就是反码,例如 8位二进制“+1”,其原码与反码都是00000001;

对于负数和“-0”而言,符号位与原码中一样,保持不变,其余位数逐位取反,1换成0,0换成1,例如 “-1”,其8位二进制原码是1000 0001,其反码是1111 1110;

 

但是 这里仍然有一个问题,就是-0 和+0 是需要分别存储的,但实际上没有意义,还要浪费空间来存储,所以反码逐渐被人类抛弃直接使用,而是改进成补码。

③ 补码的概念

补码正是基于反码的“-0”问题诞生的,可以解决这个问题。

补码的计算方法是:

正数和+0的补码是其本身,

负数则先计算其反码,然后反码加上1,得到补码。(这里还是有反码的概念在里面)

补码换算为原码的过程中,如果补码是正数或者+0的补码,则其原码就是补码本身;如果补码是负数或者-0的补码,则其原码的计算方法是,先将补码减掉1,得到反码,再将反码取反,得到原码。

由于补码1000 0000具有特殊性,计算机在编写底层算法时,将其规定为该取值范围中的最小数-128,其值与(-1)+(-127)的计算结果正好符合。

补充一点,8位二进制补码1000 0000没有对应的反码和原码,其他位数的二进制补码与此类似。

 

综上所述,在C语言中整数的计算实际上 是对应二进制的补码在计算,还有诸多规则。

 

 

2、关于进制转换的一些概念

通常是十六进制(Hex).、十进制(Dec)、八进制(Oct)和二进制(Bin),其中十六进制 = 4位二进制展开成1位进制,八进制 = 3位二进制展开成1位八进制。所以接下来仅讨论二进制 和 十进制的关系即可。

2.1 二进制数转十进制数:

二进制的一个整数或小数转十进制,可以通过在二进制中对 该数各位按十进制带权相加得到。

如: 1001.101_{(2)} = 1*2^{3} + 0*2^{2} + 0*2^{1} + 1*2^{0} + 1*2^{-1} + 0*2^{-2} + 1*2^{-3} = 9.625_{(10)}

 

2.2 十进制整数转二进制:

十进制的一个整数转二进制,可以通过在十进制中对这个整数除以2取余数得到。如4 = 1*4 + 0*2 + 0*1 = 100

 

2.3 十进制分数\小数 转二进制分数(一般都会变成分数)

十进制的分数转二进制,可以在十进制中做除法得到小数,再转二进制;或把分子分母转二进制,在二进制中做除法

 

3、 C语言的bit算法代码展示

包括有 ①取符号函数 、②取二进制最高位的1、③取二进制最低位的1、④二进制数前有多少个0、⑤二进制数有多少个1、⑥数字顺序颠倒

#include<stdio.h>

int sign(int i)
{
	// 补充说明,在C语言中如果是有符号整型 位运算右移 会保留符号
	// 如果是 无符号整形,则不会保留符号
	// 所以如果要整体移动进制 则需要用到无符号形式 
	// -1代表负数,1代表正数 
	unsigned a = -i;
	return (i>>31)|(a>>31);	
}

int reserve_highest_1(int i)
{
	unsigned a = i;
	a |= (a >>  1);
	a |= (a >>  2);
	a |= (a >>  4);
	a |= (a >>  8);
	a |= (a >> 16);
	a -= (a >> 1);
	return a;	
}

int reserve_lowest_1(int i)
{
	return i&-i;
}

int numbenr_of_leading_0(int i)
{
	int n = 0;
	unsigned a = i;
	if (a >> 16 == 0) { n += 16; a <<= 16; }
	if (a >> 24 == 0) { n +=  8; a <<=  8; }
	if (a >> 28 == 0) { n +=  4; a <<=  4; }
	if (a >> 30 == 0) { n +=  2; a <<=  2; }
	if (a >> 31 == 0) { n +=  1; a <<=  1; }
	return n;
}

int number_of_1(int i)
{
	unsigned a = i;
	a = ((a&0xAAAAAAAA) >>  1) + (a&0x55555555);
	a = ((a&0xCCCCCCCC) >>  2) + (a&0x33333333);
	a = ((a&0xF0F0F0F0) >>  4) + (a&0x0F0F0F0F);
	a = ((a&0xFF00FF00) >>  8) + (a&0x00FF00FF);
	a = ((a&0xFFFF0000) >> 16) + (a&0x0000FFFF);
	return a;

}

int reverse_order(int a)
{
	unsigned i = a;
	i = ((i&0xAAAAAAAA)>>1) | ((i&0x55555555)<<1);
	i = ((i&0xCCCCCCCC)>>2) | ((i&0x33333333)<<2);
	i = ((i&0xF0F0F0F0)>>4) | ((i&0x0F0F0F0F)<<4);
	i = ((i&0xFF00FF00)>>8) | ((i&0x00FF00FF)<<8);
	i = ((i&0xFFFF0000)>>16) | ((i&0x0000FFFF)<<16);
	return i;
} 

int main()
{
	
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值