提示:斜体表示进制标识,并非数值中的一位
public static final int MAX_VALUE = 0x7fffffff;
int在Java内存中占4个字节(32位),取值范围位 -231 ~ 231 - 1,因为总共32位,故可以表示 232 个数,一分为二,即 232 / 2,则正数和负数分别可以表示 231 个数,然而不包括0,因此需要从正数中拿一个给0腾位置,从而取正数最大值为 231 - 1。
十进制 231 对应的十六进制为 0x80000000,故十进制 231 - 1 对应的十六进制为 7fffffff
根据上述,int最大值对应的十六进制为0x7fffffff
public static final int MIN_VALUE = 0x80000000;
在了解int最小值前,需要知道一点:负数在计算机中使用的是其数值的补码保存。
十进制 231 对应的二进制为 0100,0000,…,0000(…代表5组四个0)
231 补码计算过程:其二进制取反加一
0100,0000,…,0000 -> 01011,1111,…,1111 -> 0100,0000,…,0000
final static char[] digits = {
‘0’ , ‘1’ , ‘2’ , ‘3’ , ‘4’ , ‘5’ ,
‘6’ , ‘7’ , ‘8’ , ‘9’ , ‘a’ , ‘b’ ,
‘c’ , ‘d’ , ‘e’ , ‘f’ , ‘g’ , ‘h’ ,
‘i’ , ‘j’ , ‘k’ , ‘l’ , ‘m’ , ‘n’ ,
‘o’ , ‘p’ , ‘q’ , ‘r’ , ‘s’ , ‘t’ ,
‘u’ , ‘v’ , ‘w’ , ‘x’ , ‘y’ , ‘z’
};
为方法中将整型转换为字符数组提供相应字符。
功能: 将十进制的i转换为radix进制的字符串
参数:
public static String toString(int i, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) //如果进制基数参数不在合适范围内(Character.MIN_RADIX~Character.MAX_RADIX,即2~36)
radix = 10;
/* Use the faster version */
if (radix == 10) { //十进制就直接使用另一个toString方法输出,一个参数的toString(int i)方法,是默认i为十进制。
return toString(i);
}
/**
* buf字符数组长度为33原因:
* int型占32位,故最长长度是在使用二进制时,数值为临界值,要使用32个字符分别存储每一位。
* 同时还需要一位用来存放符号,故此处字符数组长度设置为33。
*/
char buf[] = new char[33]; //定义存储int转换后的结果。此处使用char数组,其长度为33。
boolean negative = (i < 0); //保存标识是否为负数
int charPos = 32; //从char数组最后一位开始
if (!negative) { //如果为负数,先将i变为其相反数,也就是正值
i = -i;
}
while (i <= -radix) { //循环将int每一位转换为字符存储在字符数组中,从低位开始
buf[charPos--] = digits[-(i % radix)]; //此处直接使用Integer中存放进制单位数的常量数组
i = i / radix; //每获得一位,就除10
}
buf[charPos] = digits[-i];
if (negative) { //如果为负数,将符号存放到字符数组中
buf[--charPos] = '-';
}
return new String(buf, charPos, (33 - charPos));
}
总所周知,数字09有10个,字母az有26个,故共有36个,而十六进制中从10开始就是用字母从a开始表示直到f(代表15),因而假如使用所有数字和字母来表示某进制中的单位数,则最多可以表示到35,当然从0开始,故最大可表示36进制,Character.MAX_RADIX=36
一进制,只能表示一个数,不管它有多长,故一般不适用,因而最小用二进制,Character.MIN_RADIX=2
功能: 求十进制的整型val转换为shift所代指进制(shift并非直接表示进制,具体请看下述代码中的注释)存放的二进制后,从左至右首个1的左边0的个数
参数:
private static String toUnsignedString0(int val, int shift) { //shift只能为1~5,1-二进制,2-四进制,3-八进制,4-十六进制
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); //mag是val用二进制表示至少的位数
int chars = Math.max(((mag + (shift - 1)) / shift), 1); //(mag + (shift - 1)) / shift)是将二进制表示下的位数转换为其它进制表示的位数,后面1是保证位数至少为1,因为为0,后面字符数组就不可能保存val每个数字
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars); //将val转换为shift代表的进制,并保存在buf字符数组中
// Use special constructor which takes over "buf".
return new String(buf, true);
}
求val转为二进制,并使用32位存放时,从左至右第一个1前面的0个数。例如,8(10) 变为二进制 1000(10) ,共32位,第一个1在倒数第四位,故前面有28个0,故返回28。
功能: 将整数val转换为对应进制,并保存在buf字符数组中
参数:
第一个参数:要转换的十进制整数
第二个参数: 偏移量,如果转换为二进制为1,八进制为3,十六进制为4
第三个参数: 用于存储转换进之后保存的数组
第四个参数: buf开始放入数据的开始位置的偏移量
第五个参数: val二进制的长度
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift; //根据偏移量得到进制基数,假如shift=3,则1左移3为=位,得到8,此时表示八进制
int mask = radix - 1; //对应进制能使用的最大数码,八进制下为7
do {
//charPos先减1,是数组最后一个元素的下标,offset表示偏移量,一般为0表示不产生偏移,此处偏移相当于左移右移
buf[offset + --charPos] = Integer.digits[val & mask]; //val与mask进行逻辑与,同时为1结果为1
val >>>= shift; //每进行一次循环,val无符号右移一位,以便一位位将数字将转换字符存到字符数组buf中
} while (val != 0 && charPos > 0);
return charPos;
}
功能: 将字符串s转换为radix进制的整型
参数:
public static int parseInt(String s, int radix) throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
} else if (radix < 2) { //Character.MIN_RADIX,与toString(int i, int radix)中类似
throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX");
} else if (radix > 36) { //Character.MAX_RADIX
throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
} else {
boolean negative = false; //是否为负数,负数-true
int i = 0;
int len = s.length();
int limit = -2147483647; //int正数最大值为2147483647,但后面while循环每次转换int的一位数字是通过result -= digit,故而最后得到的是是负数,因而此处limit也为负值,便于后面进行范围判断
if (len <= 0) {
throw NumberFormatException.forInputString(s);
} else {
char firstChar = s.charAt(0);
if (firstChar < '0') { //小于字符0,说明第一个并未数字,可能为正负符号
if (firstChar == '-') {
negative = true;
limit = -2147483648; //int负数绝对值的最大值为2147483648,剩余与前面limit赋值同理
} else if (firstChar != '+') {
throw NumberFormatException.forInputString(s);
}
if (len == 1) {
throw NumberFormatException.forInputString(s);
}
++i;
}
//假设方法输入Integer.MIN_VALUE,即-2147483648,十进制
int multmin = limit / radix; //multmin = 214748364
int result;
int digit;
for(result = 0; i < len; result -= digit) { //循环到最后一个字符时,result=-214748364,进入循环
digit = Character.digit(s.charAt(i++), radix); //digit = 8
//判断result是否小于multmin,此时正好等于,故不满足
if (digit < 0 || result < multmin) { //result *= radix; result
throw NumberFormatException.forInputString(s);
}
//每循环一次,就乘上基数
result *= radix; //result = -214748364 * 10 = -2147483640
if (result < limit + digit) { //主要在于最后一次循环,判断是否小于int范围最小值或最大值的相反数加上最后一个字符代表的数值
throw NumberFormatException.forInputString(s);
}
}
return negative ? result : -result;
}
}
}
可以从边界值进行考虑,在parseInt方法参数是s=“-2147483648”,radix=10,即要将字符串"-2147483648"转换为十进制int值。上述for循环进入到最后一次循环时,result = -214748364,最后一字符’8’对应数字8,要将最后一个字符加入到结果中,需要先让result * 10,也就是result * 基数。然而在乘10之前,可以通过判断此时result是否已经小于multmin,假如此时result为-214748365,这个乘10后就已经超出int范围,肯定是不合理的,因此可以在result * 基数前先进行result < multmin判断,故multmin = limit / radix(当然对于正数和负数其limit是不同的,因为int范围为-231 ~ 231 - 1,即-2147483648 ~ 2147483647)。如果满足result < multmin 且digit >= 0后,就对result 乘10。
可以看成result - digit < limit,就是在进行 result -= digit 前判断最终结果是否超除int范围
欢迎在观看的各位留言,对于其中有误或不足的地方可以在评论区提出。觉得不错的可以点赞、收藏加关注,感谢各位!