Loading... # 二进制 之前认为二进制是很简单的东西,所以没很认真地去想(的确是很简单的东西,但是在计算机对它的应用中与我们想的有所不同),导致在面向google编程的过程中查询关于数据溢出的相关问题中碰壁。 例子是这样的: ```apache #include <stdio.h> int main() { short int a,b; a = 32767; b = a+1; printf("%d ,%d \n",a,b); return 0; } ``` 输出是: 32767 ,-32768 这里short int默认是占两字节(取决于解释器)能代表2^16个数字 -32768~32767 按理说 这很好理解 能代表的最大的数字加上一,超出了能表示的最大范围,应该会变成另外一个奇奇怪怪的数字。然后我就想自己试试底层二进制是怎么算的,首先首位是代表正负号,0是正数,1是负数,然后我就很想当然地这样做了: `011111111111111+0000000000000001=1000000000000000` 这他妈结果不是应该是0吗 或者说是 -0?唉,不对啊, 0为什么还能占两个位置,1000000000000000和00000000000000不都表示0吗?那他在计算机里是怎么表示2^16个数字的,不应该是(2^16) -1个吗 因为0站了两个坑。 后面我又开始了面向google编程。。。。。。 解释: 原来二进制数在计算机里有三种表示方法,原码、反码、补码。 ##### 原码: 原码就是我一直以为的二进制数,也是广义上的二进制数,快速得到一个二进制数的方法 -**除2取余法**,  然后为了表示正负数 第一位被占用,代表正负。但是源码的作用是**适合人类阅读**,并不适合计算机作运算, 还有计算机里的减法其实就是 加上负数 如 1 -1 => 1+(-1) 正数运算还好 0001+0010=0011 => 1+2 =3 但是 有负数的运算 0001+1001=1010 => 1+(-1) =-2 这明显是错的 为了**处理负数运算**,这时候用到了反码。 ##### 反码: 口诀 正数的反码是其本身(因为反码本来就是针对负数的),负数的反码是,符号位置不变,其余位置相反。 这样的话 1的反码 0001(原码)->0001(反码) -1的反码 1001(原码)->1110(反码) 此时再作这个运算 1+(-1) = 0001(反码)+1110(反码) = 1111(反码) 将1111转成原码 1000 = -0 照理说没错 1+(-1)=0 ,这样就解决了负数运算的问题, 但是这个-0越看越难受,所以**人们为了只保留一个0**,又发明了补码。 ##### 补码: 口诀 ,正数的补码是其本身,负数的补码是 ,符号位置不变,其余位置相反 再加 1 目的就是取消把-0这个位置让出来 此时我们再看0000(原码)和1000(原码) 0000(原码)->其本身 0000(补码) 1000(原码) ->1111(反码)+0001 =>10000(补码) 超出一位舍去 保留后四位 还是0000(补码) ##### 总结: 再回到刚开头的问题 32767+1 = 0111_1111_1111_1111 (原码)+ 0000_0000_0000_0001(原码) =0111_1111_1111_1111(反码)+0000_0000_0000_0001(反码) =0111_1111_1111_1111(补码)+0000_0000_0000_0001补码) =1000_0000_0000_0000(补码) 就是-32768 而-32768是1000_0000_0000_0001 能倒推出原码 而-32768 不行(因为这根本不是在表示范围内的数?所以用-0的原码来表示?) **计算机用的都是补码!!** 写补码逻辑的时候我老是会想到错位相减法-_-|| 最后修改:2021 年 08 月 08 日 09 : 55 PM © 禁止转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信