数据类型 | 字节长度 | 范围 | 包装类 |
int | 4 | -2147483648~2147483647 (-231~231-1) | Integer |
short | 2 | -32768~32767 | Short |
long | 8 | -9223372036854775808~9223372036854775807 (-263~263-1) | Long |
byte | 1 | -128~127 | Byte |
float | 4 | 32位IEEE754单精度范围 | Float |
double | 8 | 64位IEEE754双精度范围 | Double |
char | 2 | Unicode[0,65535] | Charactor |
boolean | 1 | true,false | Boolean |
1、存在这样一个数,使得 N+1<N;
乍一看这个表达式跟我们从小到大用了几十年的常理完全不符,但本篇讲到的是Java,所以,这个N肯定是Java里存在的。如果对数字敏感的同学就会发现,java中各种基本类型的范围以及表示这些数字所用到的字节数似乎跟这个有所关系。不错,如果某个类型数据所能表达的大小范围超出,那么将会变成与这个数据符号相反的数;比如 byte 128 会变成 -128,byte 129 会变成 -127,byte -129 会变成 127
byte n = 127;
byte m = (byte) 128;
System.out.println("n>m:"+(n>m));
上面代码输出 true,m等于n+1,但是m溢出了,变成了-128.也就是byte能够表达的最小值。所以上述问题,N的解应该是 Byte.MAX_VALUE、Integer.MAX_VALUE、Long.MAX_VALUE等即将超出该数据类型范围的数。
2、存在一个数N,N != 0 使得 N = -N;
有了第一个问题的解答,我们很容易可以找到一个例子: byte -128;咱们要求它的负数,先找到原码:1000 0000 ,求其负数即求其补码(求反码再加一):反码 :1111 1111 补码:1000 0000;符号位不动哈
所以 N 的解应该是Byte.MIN_VALUE、Integer.MIN_VALUE等
3、存在一个数N,使得 N = N + 1;
4、存在一个数N,使得 N = N -1;
咱俗话说,多一点不多少一点不少。转化为数学语言就是:一个无穷大的数加上一个数还是无穷大,一个无穷小的数减去一个数还是无穷小。
double n = Double.POSITIVE_INFINITY;
System.out.println(n == n+1);
double n = Double.NEGATIVE_INFINITY;
System.out.println(n == n-1);
以上两段代码的输出都是true,至于为什么呢,我们来看下这两个无穷的定义
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
5、存在一个数N,使得 N != N;
这个事情其实算不上一个绕的问题,在Java中有定义这样一个数,说是数,但定义的目的不是数。说不是数,但类型是数。这个玩意就是 NaN。
/**
* A constant holding a Not-a-Number (NaN) value of type
* {@code double}. It is equivalent to the value returned by
* {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
*/
public static final double NaN = 0.0d / 0.0;
/**
* A constant holding a Not-a-Number (NaN) value of type
* {@code float}. It is equivalent to the value returned by
* {@code Float.intBitsToFloat(0x7fc00000)}.
*/
public static final float NaN = 0.0f / 0.0f;
注意了,这个玩意可只有浮点型数据才可以这么玩哦。
6、“>>”、“>>>”、“<<”都是什么鬼?
首先 >> 、>>> 都是Java中的右移操作,“>>”是有符号右移,“>>>”是无符号右移。顾名思义,有符号须得在右移过程中保持符号,所以,有符号右移,若是正数则在高位补0,反之补1,无符号右移不管正负都补0.
特别注意点:对于char、byte、short等类型。在运算时候会先进行int类型的转换。都知道int类型4个字节,32位。所以,如果说右移的位数超过了32,那也是没有用滴。Java采用了取余数的方式来保证右移位数的有效性,即 n>>m相当于 n>>(m%32); 右移相当于做除法操作
“<<”是什么呢?它表示左移操作。那么怎么不见“<<<”这个东西呢?因为左移操作没有有符号与无符号左移。左移操作的过程就是移除最高位,然后在末尾补0就OK。比如:4,二进制100,当然运算时是3位的,
即 0000 0000 0000 0000 0000 0000 0000 0100,如果进行<<3操作,也就是左移三位,去掉头上三个0,后面再补上三个:0000 0000 0000 0000 0000 0000 0010 0000,换算成十进制就是32,所以左移也就是乘法运算。同样的,如果你的左移位数大于32位,其实际左移位数位 对32 取余数的值
7、 怎么判断一个数是不是2的n次方
0000 0000 0000 0000 0000 0000 0000 0001 20 1
0000 0000 0000 0000 0000 0000 0000 0010 21 2
0000 0000 0000 0000 0000 0000 0000 0100 22 4
0000 0000 0000 0000 0000 0000 0000 1000 23 8
根据上面的思考方式我们可能更容易想到的就是通过对 1 进行左移,然后与目标数进行对比。知道左移数大于等于目标数结束。比如目标数是 8,我们左移三位就可以判断出是2的N次方,目标数是9,左移4次后发现比目标数大。此时就表示目标数不是2的N次方。
再次观察上述数据。我们不难发现二进制数中只有一位是 1 其余全是 0;如果我们对其进行减一操作
0000 0000 0000 0000 0000 0000 0000 0000 20-1 0
0000 0000 0000 0000 0000 0000 0000 0001 21-1 1
0000 0000 0000 0000 0000 0000 0000 0011 22-1 3
0000 0000 0000 0000 0000 0000 0000 0111 23-1 7
再观察两组数据。发现好像还能做点什么,哎,如果我们将 (0&1)放在一起,一眼就可以看出结果为0,那么其他的呢。不出意外的,其他数字与上减一后的值都是0.所以这个问题又有了新的解答。
判断一个数是不是2的N次方,可以将这个数减一跟自身做与运算,结果如果为零,则表示为2的N次方,反之则不是。。这种方式相对于上一种显然具有很大的优势。
8、给定一个数,求这个数二进制中 1 的个数
0000 0000 0000 0000 0000 0000 0000 0000 20-1 0
0000 0000 0000 0000 0000 0000 0000 0001 21-1 1
0000 0000 0000 0000 0000 0000 0000 0011 22-1 3
0000 0000 0000 0000 0000 0000 0000 0111 23-1 7
首先我们还是给出这组数字。如果从下往上看,不难找到上面的数字都是可以由下面的数字通过右移得到的。所以我们可以通过一个一个的右移操作观察最后一位数字是不是1来统计个数。
public int count(int n){
int count = 0;
while(n>0){
//判断最后一位是不是1
if((n & 1)==1){
count ++;
}
n >>= 1;
}
return count;
}
对于每次 n & (n-1) 的计算,都会消除掉末尾的1,所以,上述代码还可以改写成
public static int count(int n){
int count = 0;
while(n>0){
n = n & (n-1);
count ++;
}
return count;
}
....................
后续如果发现一些有意思的关于数字的东西继续更新。。。。。