整数基础
Java中,整数都是有符号的,最高位是符号位,0表示正数,1表示负数。有四种,byte,short,int和long。
byte 8位,-2 7 ~ 2 7 - 1,-128 ~ 127, 0x80 ~ 0x7F
short 16位,-2 15 ~ 2 15 - 1,-32768 ~ 32767, 0x8000 ~ 0x7FFF
int 32位,-2 31 ~ 2 31 -1,-2147483648 ~ 2147483647, 0x8000000 ~ 0x7FFFFFFF
System.out.println(String.format("Max byte %d, 0x%X, half 0x%X", Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE/2));
System.out.println(String.format("Min byte %d, 0x%X, half 0x%X", Byte.MIN_VALUE, Byte.MIN_VALUE, (byte)(Byte.MIN_VALUE/2)));
System.out.println(String.format("Max short %d, 0x%X, half 0x%X", Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE/2));
System.out.println(String.format("Min short %d, 0x%X, half 0x%X", Short.MIN_VALUE, Short.MIN_VALUE, (short) (Short.MIN_VALUE/2)));
System.out.println(String.format("Max Int %d, 0x%X, half 0x%X", Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE/2));
System.out.println(String.format("Min Int %d, 0x%X, half 0x%X", Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE/2));
// Outputs
//Max byte 127, 0x7F, half 0x3F
//Min byte -128, 0x80, half 0xC0
//Max short 32767, 0x7FFF, half 0x3FFF
//Min short -32768, 0x8000, half 0xC000
//Max Int 2147483647, 0x7FFFFFFF, half 0x3FFFFFFF
//Min Int -2147483648, 0x80000000, half 0xC0000000
细节和原理
值得注意的是,16进制的数值与直觉预期并不一样,特别是负数。正数是一致的,比如int,一共是32位,最高位是符号,所以真正数值部分是31位,那么最大的int就是0x7FFFFFF。
但负数,也即是最小的int,却与直觉完全不一样。按照直觉,负数最高位是1,那最小的int应该是0xFFFFFFFF啊,为何确是0x80000000呢?原因就是整数的编码方式并不是直接的二进制形式的,是以 补码的形式 ,也就是说在内部实现中,用二进制表示一个整数的时候,是以二进制补码形式(转成二进制后还要求其补码,才是真实的二进制和16进制形式)。
简单来说,补码是一种二进制编码形式,正数的补码就是它的本身,而负数的补码是其取反后加1,可以 参考百科上的定义 。
负数的转换:原码取反加1,即是补码,补码减1再取反即是原码,符号位在转换过程中一直不变。
非10进制字面常量是补码形式
在日常代码中,为了方便,通常都用16进制来写一些整数常量,这里就特别要注意了。16进制的字面常量,不会再进行补码转换,会当成补码直接使用。
System.out.println(String.format("Literal %d, 0x%X", 0xffffffff, 0xffffffff));
//Literal -1, 0xFFFFFFFF
所以,你写的0xFFFFFFFF是补码形式,它的原码是减1再取反,(32个1)减1,最低位变成0,前面31个1,再取反,就只剩下最后一位是1和最高位的符号位,因此是-1,注意符号位是不变的,在转换过程中。
而最小的整数是-2 31 ,原码 形式应该是0x80000000,先取反变成了0xFFFFFFFF,再加1,符号位最高位不变的情况下,其余全变成了0,所以是0x80000000。
最小整数
开篇时说了,当时错误的认为0xFFFFFFFF是最小的整数,这里犯的第一个严重错误是,误把二进制的原码当成了补码,代码中的16进制(二进制)都是补码形式的,它的原码是0x10000001即-1。这个错误是比较明显的。
但另外的问题就是,假如都是二进制原码的情况下,为啥最小的整数是0x80000000而不是0xFFFFFFFFF。这是理解上的误区,整数的定义是,最高位是符号位,所以常规认知是全是1的情况是最大的数,加上符号不就变成最小的了么?这是以10进制思维,也就是二进制转换成为10进制后的想法。计算机只认识二进制,在最高位是1(负数)的情况下,哪个数最小?当然0x80000000最小啊,它除了符号位全是0,肯定 小于0xFFFFFFFF,因此从二进制的角度来理解,0x80000000是最小的整数。
而0xFFFFFFFF(原码)则是最大的负整数,最高位是符号位,其余31位全是1,肯定 最大啊,当然 了最大的负数是-1。
一些有意思的值
Integer.MAX_VALUE + 1 = Integer.MIN_VALUE
按理说应该溢出了,但如果以16进制去计算,就是这样的结果:0x7FFFFFFF + 1 = 0x80000000
System.out.println(String.format("Max in %d (0x%X) + 1 = %d (0x%X)", Integer.MAX_VALUE, Integer.MAX_VALUE, (Integer.MAX_VALUE+1), (Integer.MAX_VALUE+1)));
// Max in 2147483647 (0x7FFFFFFF) + 1 = -2147483648 (0x80000000)
Integer.MIN_VALUE - 1 = Integer.MAX_VALUE
System.out.println(String.format("Min in %d (0x%X) - 1 = %d (0x%X)", Integer.MIN_VALUE, Integer.MIN_VALUE, (Integer.MIN_VALUE-1), (Integer.MIN_VALUE-1)));
//Min in -2147483648 (0x80000000) - 1 = 2147483647 (0x7FFFFFFF)
网友评论