十进制小数转换成二进制的原理理解

转载自https://segmentfault.com/a/1190000013606224

十进制小数转换成二进制的原理理解 在学习浮点数据类型的时候,涉及到了10进制的小数如何转成2进制数的问题(此文章不讨论精度问题,仅涉及转换原理–乘2取整).学习到的方法是“乘2取整“,但是一直不知道具体原理是什么,现在从数学上说明一下原理

乘2取整的操作方法 将十进制的小数部分乘2,将所得结果的整数位作为二进制的位。舍弃乘2所得结果的整数部分,如果剩余部分为0,计算结束。否则继续乘2,进行取整操作(所得整数位继续向右添加)。(如果出现循环,则终止计算,写成循环小数格式,或根据精度位数要求,保留结果位数,停止计算) 举例说明:

十进制数:0.25

  1. 0.25 x 2 = 0.5 二进制数 : 0.0

  2. 0.5 x 2 = 1.0 二进制数 : 0.01

结果二进制数: 0.01 原理解释 十进制数的小数部分 M 对应二进制部分应为 A1 * 2^(-1) + A2 * 2^(-2) + … + An * 2^(-n) 其中An 为 0 或 1

现在将 M 乘 2 :M * 2 = A1 * 2^0 + A2 * 2^-1 + … + An * 2^(1-n)

此时,A1 * 2^0 = A1 即为 M * 2的整数部分的值 求出了A1.

然后舍弃M * 2 的整数部分,即舍弃了 A1. M * 2 - A1 = A2 * 2^-1 + … + An * 2^(1-n)

最后不断重复这一计算方式,直到乘2所得的结果小数部分为0(或者达到要求精度的位数)为止,二进制小数即为: 0.A1A2..Am (为0时m=n,否则根据精度要求决定m大小)

负数的补码

要求解一个负数的补码,如果能知道这个数的“模”,再用“模”减去这个数的绝对值,得到的这个数就是这个负数的补码

“模”是指一个计量系统的计数范围。如时钟等。计算机也可以看成一个计量机器,它也有一个计量范 围,即都存在一个“模”。

例如:
时钟的计量范围是0~11,模=12。

表示n位的计算机计量范围是0~2(n)-1,模=2(n)。【注:n表示指数】
“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。

例如: 假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:
一种是倒拨4小时,即:10-4=6
另一种是顺拨8小时:10+8=12+6=6
在以12模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替。 对“模”而言,8和4互为补数。实际上以12模的系统中,11和1,10和2,9和3,7和5,6和6都有这个特性。共同的特点是两者相加等于模。

这样就很清晰了,甚至这里的“模”跟我们学习除法的模和计算机中的mod都扯上了关系~~

补码原理

设我们有一个 4 位的计算机,则其计量范围即模是 2^4 = 16,所以其能够表示的范围是0~15,现在以计算 5 - 3为例,我们知道在计算机中,加法器实现最简单,所以很多运算最终都要转为加法运算,因此5-3就要转化为加法:

/ 按以上理论,减一个数等于加上它的补数,所以 5 - 3 / 等价于 5 + (16 - 3) // 算术运算单元将减法转化为加法 / 用二进制表示则为: 0101 + (10000 - 0011) / 等价于 0101 + ((1 + 1111) - 0011) / 等价于 0101 + (1 + (1111 - 0011)) / 等价于 0101 + (1 + 1100) // 括号内是3(0011)的反码+1,正是补码的定义 / 等价于 0101 + 1101 / 所以从这里可以得到 -3 = 1101 / 即 -3 在计算机中的二进制表示为 1101,正是“ -3 的正值 3(0011)的补码(1101)”。 / 最后一步 0101 + 1101 等于 10010

原文:https://blog.csdn.net/leonliu06/article/details/78685197


刚刚提到了模的概念,现在看看计算机补码和模的关系。

对于计算机,其概念和方法完全一样。n位计算机,设n=8, 所能表示的最大数是11111111,若再
加1称为100000000(9位),但因只有8位,最高位1自然丢失。又回了00000000,所以8位二进制系统的
模为2(8)。 在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。

把补数用到计算机对数的处理上,就是补码。

另外两个概念
一的补码(one’s complement) 指的是正数=原码,负数=反码。 而二的补码(two’s complement) 指的就是通常所指的补码。

附:补码的代数解释
任何一个数a都可以被表示为:

-a=2^(n-1)-2^(n-1)-a;

假设a为正数,那么-a就是负数。而根据二进制转十进制数的方法,我们可以把a表示为:

a = k02^0 + k12^1 + k22^2 +……+ k(n-2)2^(n-2)

这里k0,k1,k2,k(n-2)是1或者0,而且这里设a的二进制位数为n位,即其模为2^(n-1),而2^(n-1)其二项展开是:

1+2^0+2^1+2^2+……+2^(n-2)

,而将式子-a=2^(n-1)-2^(n-1)-a中的,2^(n-1)-a代入

a=k02^0+k12^1+k22^2+……+k(n-2)2^(n-2)

2^(n-1)=1+2^0+2^1+2^2+……+2^(n-2)

,得到了

2^(n-1)-a = (1- k(n-2))*2^(n-2) + (1- k(n-3))*2^(n-3) +……+ (1- k2)*2^2 + (1- k1)*2^1 + (1- k0)*2^0 +1

这步转化正说明了取反再加1的规则的代数原理所在。

原理: 因为这里k0,k1,k2,k3……不是0就是1,所以1-k0,1-k1,1-k2的运算就是二进制下的取反,而为什么要加1,追溯起来就是2^(n-1)的二项展开式最后还有一项1的缘故。而-a=2^(n-1)-2^(n-1)-a中,还有-2^(n-1)这项未解释,这项就是补码里首位的1,首位1在转化为十进制时要乘上2^(n-1),这正是n位二进制的模。

补码与模

绕了这么一圈,终于回来了,万变不离其宗,总要有一个汇聚的时候。看看补码与模到底有什么关系 我们再来看一下刚刚推导公式:

2^(n-1)-a = (1- k(n-2))*2^(n-2) + (1- k(n-3))*2^(n-3) +……+ (1- k2)*2^2 + (1- k1)*2^1 + (1- k0)*2^0 +1

其实意思都在里面了, 总结: (1)左边的2^(n-1)就是二进制的“模”,如:8位二进制数,模为2^7=128 (2)左边的 a 是一个数的源码 (3)右边所有的就是 a 的补码(取反+1)

这里说明了:一个数(负数)的

模 = 原码 + 补码

例: a[原] = -16D = -001 0000B = 1001 0000[计] a[反] = 1110 1111 a[补] = 1111 0000

反过来看,去掉符号位

模 = a[原] + a[补],即:128D = 001 0000 + 111 0000

因为对于一个数,计算机中补码是唯一的,所以可以用补码表示一个数 而且这里还有一个问题,就是可以用-0来表示-128,因为-0和-128互为补码

float 的存储方式

v=(-1)s * M * 2E

IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

E位的编码形式并非常用的补码形式(正数是它本身,负数符号位变1数据取反加一),而是把E位的8位能代表的数据空间(0-255)左右分为两半,以127为中点,代表0。如果指数为是1,则E位是128;如果指数位是2,则E位是129;如果指数位是-1,则E位是126,以此类推。

为什么要将指数位分为两部分呢?这样就可以表示-n次方了

https://www.cnblogs.com/zhugehq/p/5918599.html

还是不太懂

参考1. https://www.cnblogs.com/zhugehq/p/5918599.html 参考2. https://www.cnblogs.com/SimpleISP/p/5280362.html参考2. https://www.cnblogs.com/SimpleISP/p/5280362.html

字节序

字节序也称为字节顺序,在计算机中对数值的存储有一定的标准,而该标准随着系统架构的不同而不同。了解字节存储顺序对于逆向工程是一项基础知识,在动态分析程序的时候,往往需要观察内存数据的变化情况,这就需要我们在掌握数据的存储宽度、范围之后,进一步了解字节顺序。

通常情况下,数值在内存中存储的方式有两种,一种是大尾方式,另一种是小尾方式。关于字节序的知识,通过一个简单的例子就可以掌握。

比如有0x01020304(C语言中对十六进制数的表示方式)这样一个数值,如果用大尾方式存储,其存储方式为01 02 03 04,而用小尾方式进行存储则是04 03 02 01

字节顺序是指多字节的值在硬件中的存储顺序,一般分为大端(big-endian[‘endɪən]字节存储次序)和小端(little-endian) 大端:先存储高字节,或者说,高字节存储在低地址,低字节存储在高地址 小端:先存储低字节,或者说,低字节存储在低地址,高字节存储在高地址

大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。 小端字节序:低位字节在前,高位字节在后,即以0x1122形式储存。

通常情况下,Windows操作系统兼容的CPU为小尾存储方式,而Unix操作系统兼容的CPU多为大尾存储方式。在网络中传输的数据的字节顺序使用的是大尾存储方式。

首先,为什么会有小端字节序?

答案是,计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。

但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。

计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读第一个字节,再读第二个字节。

如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节。小端字节序正好相反。

理解这一点,才能理解计算机如何处理字节序。

字节序的处理,就是一句话:

“只有读取的时候,才必须区分字节序,其他情况都不用考虑。”

处理器读取外部数据的时候,必须知道数据的字节序,将其转成正确的值。然后,就正常使用这个值,完全不用再考虑字节序。

即使是向外部设备写入数据,也不用考虑字节序,正常写入一个值即可。外部设备会自己处理字节序的问题。

比较形象的说明 https://www.cnblogs.com/zhugeanran/p/8513264.html

主机字节序

不同的CPU有不同的字节序类型,这些字节序是指 整数 在内存中保存的顺序,这个叫做 主机序。

最常见的有两种: 1.Little endian:将低序字节存储在起始地址 2.Big endian:将高序字节存储在起始地址

x86系列CPU都是little-endian的字节序。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

https://www.cnblogs.com/52php/p/6114080.html