1.toBinaryString方法的实现
1 public static String toBinaryString(int i) { 2 return toUnsignedString0(i, 1); 3 } 4 private static String toUnsignedString0(int val, int shift) { 5 // assert shift > 0 && shift <=5 : "Illegal shift value"; 6 int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); 7 int chars = Math.max(((mag + (shift - 1)) / shift), 1); 8 char[] buf = new char[chars]; 9 10 formatUnsignedInt(val, shift, buf, 0, chars); 11 12 // Use special constructor which takes over "buf". 13 return new String(buf, true); 14 }
Integer.SIZE
这是用来二进制补码形式表示 int 值的比特位数。
简单提下为什么需要用2进制的补码来表示呢?
简单的来说,补码就是取反加1以方便把减法当作加上带负号的数进行加法运算。
在计算机系统中,数值一律用补码进行储存。 主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补
码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
int是4字节,一共是32位。所以Size是常数为32
Integer.numberOfLeadingZeros(val);
在指定 int 值的二进制补码表示形式中最高位(最左边)的 1 位之前,返回零位的数量
那么 mag的含义就是为了进行运算,排除掉数值的2进制中无关位的影响(int是4字节32位)
chars什么含义并没有看太懂 我们先且试一个数
例如9(00000000 00000000 00000000 0000 1001)
mag=4 chars=4暂时还是不明白chars 和shift的用途,后面对比8进制 16进制转换的代码 就会发现shift取值为1 3 4就是后文模运算(&运算)的关键之一。(2^1=2 2^3=8 2^4=16))
继续向下读代码会发现formatUnsignedInt是实现转化的核心函数
1 formatUnsignedInt(val, shift, buf, 0, chars); 2 static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { 3 int charPos = len; 4 int radix = 1 << shift; 5 int mask = radix - 1; 6 do { 7 buf[offset + --charPos] = Integer.digits[val & mask]; 8 val >>>= shift; 9 } while (val != 0 && charPos > 0); 10 11 return charPos; 12 }
对比一下参数的传递很容易得到chars对应len 长度
这个计算长度的原理是什么呢?
mag + (shift - 1)) / shift mag是val二进制的长度,最高位一定为1 我们举八进制的例子来说明
val在2进制中长为mag因为2^3=8 所以二进制中的3位就相当于8进制中的一位(这和Ip点分十进制又非常类似)
shoft-1则是为了处理10 11 位的情况 10/3=3而实际情况是10位的val该是4位的八进制数
终上:
chars = Math.max(((mag + (shift - 1)) / shift), 1);
radit<<shift等于2的shift次方。也就是radit对应到底是几进制转化
mask = radix - 1;就是进制转化对应的模的大小
需要注意的是这和求补码的模不一样 这个模是为了得出val最后几位 对应的进制的值
例如 8(0000 1000)%7(0000 0111) =1 buf 的最后一位的值就是1
do while(当value不为0并且长度>0时)循环里做什么呢?
buf[offset + --charPos]这和高精度运算很有几分相似,都是从末尾开始写入值。
通过把10进制的数通过位运算后 一位一位的写入buf这个数组中来 来实现进制的转换。
而val & mask 这个运算实际上就是模2运算、模8运算、模16运算( 后文附上对模与补码理解)。
Integer.digits[]在api中没有找到介绍,网上搜索也只找到integerDigits[n] integerDigits[n,2]的介绍。结合integerDigits[n,2]的介绍和运行知道Integer.digits[]的作用
例如9模2后为00000000 00000000 00000000 0000 0001 应该是只取出最后一位并转换成char型 存入buf数组中
最后一个string(buf,true)完成进制的转化输出。
文末附上对模的认识
模的概念:把一个计量单位称之为模或模数。例如在模为16的系统中-10其实和+6带来的影响是一样的,即
10和6在模12的系统中互为补数(补码)。计算机的硬件结构中只有加法器,所以大部分的运算都必须最终转换为加法。
就采用的模的概念对原码,反码进行改进以方便运算,即计算机中的运算都是补码之间的加法运算。个人认为:模的概念在2进制中就是和按位取反是一个东西。模概念的出现就是为了人直观的进行反码计算.
我们知道补码是反码加1 例如:-9(10001001)--(11110110)--(111101
11)实际就是-9的模128后的2进制+1,为什么是模128,计算机的计数器是8位的,第一位是符号位所以8位实际能记得数就是256-2^7=128
a mod b = - (-a mod b) -9模128后为(11110110).
那补码和模到底什么关系呢?
模的出现是为了方便计算机对负数直接进行带上符号位的加法运算。模到底是如何实现这一功能的
在计算机网络的IP地址的点分十进制转换中经常会发现
255=2^7+2^7-1;2^7=2^6+2^5…………2^1+2^0+1 2^k=2^(k-1)+2^(k-2)+…………+2^1+2^0+1 //这可以通过迭代法来证明 2^k=(1+1)*2^(k-1)=2^(k-1)+2^(k-1) //用k-1 去替代k即k=k-1 2^(k-1)=2^(k-2)+2^(k-2) 以此类推会有2^k=2^(k-1)+2^(k-2)+…………+2^1+2^1 即原式得证
这跟模与补码有什么联系呢?
假如有一个正数a=flag1*2^(n-1)+flag2*2^(n-2)+ …… +flag(n-1)*2^1+flag(n)*2^0
其中flag取0或者1
1 a=2^n-2^n+a
//正数的原,反,补一致没有讨论的必要
-a=2^n-2^n-a
//替换右边的一个2^n和a,会有如下等式
2^n-a=2^(n-1)+2^(n-2)+…………+2^1+2^0+1-(flag1*2^(n-1)+flag2*2^(n-2)+ …… +flag(n-1)*2^1+flag(n)*2^0)
2^n-a=(1-flag1)*2^(n-1)+(1-flag2)*2^(n-2)+…………+(1-flagn)*2^0+1
(1-flag1)*2^(n-1)+(1-flag2)*2^(n-2)+…………右边这一行:由于flag是只能取0.1实际这就是按位取反最后+1
2^n-a:2的n次方就是模这点上文已经提到
终上所述:模-原码=反码
对于第一符号位的处理和把符号位当作数值位进行运算的理解尚且不足 第一篇博客 水平有限 诸君见谅