java 无符号 类型_java中符号类型和无符号类型的问题分析
一 參考博文
二 java中的無(wú)符號(hào)數(shù)和有符號(hào)數(shù)
在計(jì)算機(jī)中,可以區(qū)分正負(fù)的類(lèi)型,稱(chēng)為有符號(hào)類(lèi)型,無(wú)正負(fù)的類(lèi)型,稱(chēng)為無(wú)符號(hào)類(lèi)型。
使用二進(jìn)制中的最高位表示正負(fù)
計(jì)算機(jī)中用補(bǔ)碼表示數(shù)值;另外,用二進(jìn)制的最高位表示符號(hào),0表示正數(shù)、1表示負(fù)數(shù)。
無(wú)符號(hào)和有符號(hào)數(shù)的范圍的區(qū)別
無(wú)符號(hào)數(shù)中,所有的位都用于直接表示該值的大小;有符號(hào)數(shù)中最高位用于表示正負(fù),所以,正值時(shí),該數(shù)的最大值就會(huì)變小:
無(wú)符號(hào)數(shù):1111 1111 值:255
有符號(hào)數(shù):0111 1111 值:127
同樣一個(gè)字節(jié),無(wú)符號(hào)的最大值是255,有符號(hào)的最大值是127
三 java中的基本類(lèi)型
Java的原始類(lèi)型里除了char是無(wú)符號(hào)類(lèi)型之外,其他都是有符號(hào)數(shù)據(jù)類(lèi)型,如果需要某個(gè)寬度的無(wú)符號(hào)類(lèi)型,可以用>>>進(jìn)行轉(zhuǎn)化,這個(gè)是java的無(wú)符號(hào)右移操作符,或者使用下一個(gè)寬度的帶符號(hào)類(lèi)型來(lái)模擬,
例如,需無(wú)符號(hào)的short,就用int來(lái)模擬:
int toUnsigned(short s) {
return s & 0x0FFFF;
}
java中十進(jìn)制的字面常理只有一個(gè)特性,就是所有的十進(jìn)制字面常量都是正數(shù),如果想寫(xiě)一個(gè)負(fù)的十進(jìn)制,則需要在正的十進(jìn)制字面常量前面加上“-”就好了。
但是十六進(jìn)制或者八進(jìn)制的字面常量就不一定是正數(shù)或者負(fù)數(shù),如果最高位是1,那么就是負(fù)數(shù):
System.out.println(0x80);//128
//0x81看作是int型,最高位(第32位)為0,所以是正數(shù)
System.out.println(0x81);//129
System.out.println(0x8001);//32769
System.out.println(0x70000001);//1879048193
//字面量0x80000001為int型,最高位(第32位)為1,所以是負(fù)數(shù)
System.out.println(0x80000001);//-2147483647
//字面量0x80000001L強(qiáng)制轉(zhuǎn)為long型,最高位(第64位)為0,所以是正數(shù)
System.out.println(0x80000001L);//2147483649
四 補(bǔ)碼與真值
這里先看一個(gè)問(wèn)題:
@Test
public void test01(){
System.out.println(0x80000000); // -2147483648
}
這個(gè)結(jié)果是怎么得來(lái)的?
要搞明白這個(gè)問(wèn)題,得先明白幾個(gè)概念:
機(jī)器數(shù):
一個(gè)數(shù)在計(jì)算機(jī)中的二進(jìn)制表示形式, 叫做這個(gè)數(shù)的機(jī)器數(shù)。機(jī)器數(shù)是帶符號(hào)的,在計(jì)算機(jī)用一個(gè)數(shù)的最高位存放符號(hào), 正數(shù)為0, 負(fù)數(shù)為1.
比如,十進(jìn)制中的數(shù) 3 ,計(jì)算機(jī)字長(zhǎng)為8位,轉(zhuǎn)換成二進(jìn)制就是00000011。如果是 -3 ,就是 10000011 。那么,這里的 00000011 和 10000011 就是機(jī)器數(shù)
真值:
因?yàn)榈谝晃皇欠?hào)位,所以機(jī)器數(shù)的形式值就不等于真正的數(shù)值。例如上面的有符號(hào)數(shù) 10000011,其最高位1代表負(fù),其真正數(shù)值是 -3 而不是形式值131(10000011轉(zhuǎn)換成十進(jìn)制等于131)。
所以,為區(qū)別起見(jiàn),將帶符號(hào)位的機(jī)器數(shù)對(duì)應(yīng)的真正數(shù)值稱(chēng)為機(jī)器數(shù)的真值(即補(bǔ)碼表示的值)。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
計(jì)算真值
就拿-3來(lái)說(shuō),機(jī)器數(shù)為 10000011,那么補(bǔ)碼是 11111101,所以真值就是補(bǔ)碼的值:
補(bǔ)碼求值公式:補(bǔ)碼的最高位有效位乘以(-1),然后按一般求二進(jìn)制的方法求值
例如:
-3的補(bǔ)碼 11111101 = (-1)12^7 + 12^6+.... 12^0 = -3
3的補(bǔ)碼 00000011 = (-1)027+........1*20= 3
0x80000000問(wèn)題解析
再來(lái)看0x80000000為什么等于-2147483648,Java中用此十六進(jìn)制表示int的最小值:
/**
* A constant holding the minimum value an {@code int} can
* have, -231.
*/
@Native public static final int MIN_VALUE = 0x80000000;
此十六進(jìn)制數(shù)內(nèi)存中存儲(chǔ)的的確是0x80000001的二進(jìn)制碼。因?yàn)槭褂檬M(jìn)制給int賦值時(shí),這里的十六進(jìn)制是補(bǔ)碼形式。
也就是說(shuō),我們給變量賦的是補(bǔ)碼,不是源碼,所以會(huì)直接把0x80000001這個(gè)補(bǔ)碼存入內(nèi)存
補(bǔ)碼求值得: 0x80000000 = (-1)1231+.....+0*20 = -2147483648
所以這個(gè)值是這樣來(lái)的!
五 java中的數(shù)據(jù)類(lèi)型符號(hào)擴(kuò)展
先看一個(gè)jdk源碼中int轉(zhuǎn)為long用到的方法:
@Test
public void test03(){
final long l = -5 & 0xffffffffL;
System.out.println(l); // 4294967291
}
如果運(yùn)算一個(gè)操作數(shù)是long型,而另一個(gè)操作數(shù)是int類(lèi)型。為了執(zhí)行該計(jì)算,Java將int類(lèi)型的數(shù)值用拓寬原生類(lèi)型轉(zhuǎn)換提升為long類(lèi)型,然后對(duì)兩個(gè)long類(lèi)型數(shù)值相加。
因?yàn)閕nt是有符號(hào)的整數(shù)類(lèi)型,所以這個(gè)轉(zhuǎn)換執(zhí)行的是符號(hào)擴(kuò)展。
-5 轉(zhuǎn)換為long再轉(zhuǎn)換為二進(jìn)制,0xffffffff轉(zhuǎn)換為二進(jìn)制
進(jìn)行與運(yùn)算:
1111111111111111111111111111111111111111111111111111111110000101
0000000000000000000000000000000011111111111111111111111111111111
---------------------------------------------------------------------- & 與運(yùn)算,兩個(gè)都為1才為1,否則為0
0000000000000000000000000000000011111111111111111111111110000101= 4294967173 (十進(jìn)制)
為什么-5轉(zhuǎn)long前面要補(bǔ)1呢,這里就需要知道符號(hào)擴(kuò)展規(guī)則:
窄的整型轉(zhuǎn)換成較寬(字節(jié)數(shù)多)的整型時(shí)符號(hào)擴(kuò)展規(guī)則:
如果最初的數(shù)值類(lèi)型是有符號(hào)的,那么就執(zhí)行符號(hào)擴(kuò)展(即如果符號(hào)位為1,則擴(kuò)展為1,如果為零,則擴(kuò)展為0);
如果它是char,那么不管它將要被提升成什么類(lèi)型,都執(zhí)行零擴(kuò)展,
如果將一個(gè)char數(shù)值c轉(zhuǎn)型為一個(gè)寬度更寬的整型,并且希望有符號(hào)擴(kuò)展,那么就先將char轉(zhuǎn)型為一個(gè)short,它與char上個(gè)具有同樣的寬度,但是它是有符號(hào)的
寬的整型轉(zhuǎn)換成窄的整型直接截取低位的值,高位扔掉
所以上面-5符號(hào)是1,所以進(jìn)行符號(hào)擴(kuò)展前面都補(bǔ)1,補(bǔ)成long(64位),再進(jìn)行位運(yùn)算得出結(jié)果!
六 Java中byte轉(zhuǎn)換int時(shí)與0xff進(jìn)行與運(yùn)算的原因
jdk源碼中byte轉(zhuǎn)int用到了 & 0xff,比如String的API:
public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return (char)(value[index] & 0xff);// 先轉(zhuǎn)int,再轉(zhuǎn)char
}
這里為什么要用與運(yùn)算呢? 因?yàn)閏har是無(wú)符號(hào)類(lèi)型,所以不能進(jìn)行符號(hào)擴(kuò)展,需要零擴(kuò)展,即前面補(bǔ)0
窄整型->寬整型要進(jìn)行符號(hào)擴(kuò)展,這里byte->cahr是窄到寬,如果不想進(jìn)行符號(hào)擴(kuò)展,則需要&0xff處理,先轉(zhuǎn)int消除掉符號(hào)擴(kuò)展,再轉(zhuǎn)char即可
(b & 0xff)的結(jié)果是32位的int類(lèi)型,前24被強(qiáng)制置0,后8位保持不變,然后轉(zhuǎn)換成char型時(shí),直接截取后16位。這樣不管b是正數(shù)還是負(fù)數(shù),轉(zhuǎn)換成char時(shí),都相當(dāng)于是在左邊補(bǔ)上8個(gè)0,即進(jìn)行零擴(kuò)展而不是符號(hào)擴(kuò)展
至于為什么要進(jìn)行零擴(kuò)展: 因?yàn)閏har是無(wú)符號(hào)類(lèi)型,他會(huì)把 1111 1111 當(dāng)做65535而不是-1,,所以你前面補(bǔ)1的話(huà)數(shù)就會(huì)變很大,所以這里需要進(jìn)行0擴(kuò)展,于是 & 0xff這種騷操作就來(lái)了,這里確實(shí)有點(diǎn)繞!如果不看源碼(并且要認(rèn)真看啊,哈哈)一般發(fā)現(xiàn)不了這種問(wèn)題
再比如下面代碼:
@Test
public void test01(){
byte b=-1;
System.out.println((int)b); // -1
System.out.println(b & 0xff); // 255
}
這里第二行255應(yīng)該都好說(shuō),高位清零就是,至于直接強(qiáng)轉(zhuǎn)為-1,那么符號(hào)擴(kuò)展之后補(bǔ)碼為11111111111111111111111111111111,求出結(jié)果原碼:100000000000000000000000000001 還是-1,所以就是上面的結(jié)果,原理就是這樣!
主要就是一個(gè)符號(hào)擴(kuò)展延伸出來(lái)的問(wèn)題!
總結(jié)
以上是生活随笔為你收集整理的java 无符号 类型_java中符号类型和无符号类型的问题分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java 鉴权_我爱java系列之---
- 下一篇: java异常 子类_Java异常 Exc