數(shù)的表示
一個(gè)字節(jié)為什么是8位
在計(jì)算機(jī)中數(shù)據(jù)用二進(jìn)制來(lái)保存,每一位用0和1表示,一個(gè)數(shù)據(jù)可能由多個(gè)0和1組成沈善。在當(dāng)今世界中乡数,我們通常將8個(gè)二進(jìn)制位稱為一個(gè)字節(jié)。至于為什么闻牡,是因?yàn)檫@樣的長(zhǎng)度能夠滿足對(duì)大多數(shù)字符和數(shù)字的編碼净赴。
ASCII編碼系統(tǒng)是美國(guó)統(tǒng)一的標(biāo)準(zhǔn),對(duì)美國(guó)人來(lái)說(shuō)罩润;所有字符包括單詞玖翅,標(biāo)點(diǎn)符號(hào)都全部都囊括進(jìn)了這128(0~127)個(gè)字符,美國(guó)人用完全就夠了割以。
但是金度,一個(gè)字節(jié)可以表示的信息有限,因此一個(gè)數(shù)據(jù)可以由多個(gè)字節(jié)組成严沥,以便表示更大的編碼集猜极,例如中文編碼。
最高有效位 與 最低有效位
當(dāng)一個(gè)字節(jié)用來(lái)表示數(shù)字時(shí)祝峻,像十進(jìn)制表示數(shù)字一樣魔吐,數(shù)位由左往右遞減,最左邊的是最高位莱找,最右邊的是最低位酬姆,這就出現(xiàn)了最高有效位與最低有效位的概念:
- 最高有效位(MSB):一個(gè)字節(jié)的最左邊一位
- 最低有效位(LSB):一個(gè)字節(jié)的最右邊一位
字節(jié)序 與 大端/小端模式
當(dāng)一個(gè)數(shù)字由多個(gè)字節(jié)來(lái)表示,那么每個(gè)字節(jié)的排位就有講究了奥溺,這個(gè)順序叫做字節(jié)序辞色。
例如Java中,一個(gè)整數(shù)32位浮定,存放在4個(gè)字節(jié)中相满,那么存放高位數(shù)字的字節(jié)稱為高字節(jié),存放低位數(shù)字的字節(jié)為低字節(jié)桦卒。在內(nèi)存中立美,字節(jié)存放的地址由低到高遞增。字節(jié)序決定了越高的字節(jié)存放在越低的地址方灾,還是存放在越高的地址建蹄。
大端模式或高字節(jié)序:
(符合日常的從左往右閱讀的習(xí)慣,網(wǎng)絡(luò)傳輸采用的字節(jié)序)
高字節(jié)存放在低地址裕偿,低字節(jié)存放在高地址
小端模式或低字節(jié)序:
(x86架構(gòu)處理器采用的字節(jié)序)
低字節(jié)存放在低地址洞慎,高字節(jié)存放在高地址
例如:整數(shù)127,十六進(jìn)制表示為0x0000007F
低地址 ---> 高地址
大端 00 00 00 7F
小端 7F 00 00 00
從程序去理解:
#int轉(zhuǎn)byte數(shù)組(低字節(jié)序)
public static byte[] toLittleEndian(int n){
byte[] b = new byte[4];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >>8 & 0xff);
b[2] = (byte) (n >>16 & 0xff);
b[3] = (byte) (n >>32 & 0xff);
}
#byte數(shù)組轉(zhuǎn)int(高字節(jié)序)
public static int toInt(byte[] bigEndian){
int n = 0;
for(int i=0;i<bigEndian.length;i++){
n += bigEndian[i] << ((3-i)*8);
}
}
==對(duì)字節(jié)序的思考==
只有當(dāng)一個(gè)數(shù)據(jù)由多個(gè)字節(jié)組成時(shí)嘿棘,才會(huì)有字節(jié)順序的問(wèn)題劲腿。字節(jié)序-百度百科
一個(gè)數(shù)據(jù)可以
- 編譯后保存在二進(jìn)制可執(zhí)行文件中,運(yùn)行時(shí)讀取
- 運(yùn)行時(shí)鸟妙,通過(guò)運(yùn)算產(chǎn)生焦人,存儲(chǔ)在內(nèi)存中
- 從字節(jié)流IO中讀取挥吵,組裝成CPU識(shí)別的數(shù)據(jù)
- 向字節(jié)流IO中寫入,傳輸?shù)酵獠?網(wǎng)絡(luò)或磁盤)
對(duì)于第一種垃瞧,產(chǎn)生一個(gè)代碼跨平臺(tái)的問(wèn)題蔫劣,同一份代碼,經(jīng)過(guò)同樣的編譯后个从,在不同平臺(tái)上(主機(jī)字節(jié)序不同)脉幢,運(yùn)行的結(jié)果可能不一樣。因?yàn)橥唤M字節(jié)嗦锐,按不同字節(jié)序的解析可能得到不同的數(shù)據(jù)嫌松。因此,同一份代碼需要根據(jù)不同的平臺(tái)奕污,進(jìn)行有針對(duì)性的編譯萎羔。參考使用Intel編譯器解決字節(jié)序問(wèn)題
對(duì)于第二種,數(shù)據(jù)始終保留在本機(jī)內(nèi)存中碳默,沒(méi)有字節(jié)順序不同的問(wèn)題
對(duì)于第三種贾陷,無(wú)論是讀文本文件、二進(jìn)制文件嘱根,還是網(wǎng)絡(luò)流量髓废,一個(gè)數(shù)據(jù)首先是需要經(jīng)過(guò)IO字節(jié)流,然后讀入內(nèi)存该抒,然后根據(jù)約定的格式慌洪,讀取若干個(gè)字節(jié),解析并轉(zhuǎn)化為數(shù)據(jù)凑保。因此這個(gè)約定的格式非常重要冈爹,格式中一個(gè)重要的點(diǎn)就是字節(jié)序(其次還有結(jié)束標(biāo)志等)。
對(duì)于網(wǎng)絡(luò)傳輸格式的約定:
網(wǎng)絡(luò)字節(jié)順序是TCP/IP中規(guī)定好的一種數(shù)據(jù)表示格式欧引,它與具體的CPU類型频伤、操作系統(tǒng)等無(wú)關(guān),從而可以保證數(shù)據(jù)在不同主機(jī)之間傳輸時(shí)能夠被正確解釋芝此。網(wǎng)絡(luò)字節(jié)順序采用big endian(大端)排序方式剂买。
對(duì)于文本文件:
例如,UTF-16編碼方式是兩個(gè)字節(jié)癌蓖,那么就存在字節(jié)順序的問(wèn)題,因此UTF-16會(huì)分Big-Endian和Little-Endian兩種格式的編碼婚肆,根據(jù)文件最前面的BOM——Byte Order Mark(字節(jié)序標(biāo)記)進(jìn)行區(qū)分(參考UTF的字節(jié)序和BOM )租副,F(xiàn)E、FF是編碼中不存在的兩個(gè)字節(jié)较性,因此可以用FEFF來(lái)表示大端順序用僧,F(xiàn)FFE表示小端順序结胀。UTF-8編碼不存在這樣的問(wèn)題,因?yàn)樗烊坏乜纱_定字節(jié)順序的责循,參考UTF-8編碼規(guī)則糟港。
對(duì)于二進(jìn)制文件:
需要約定文件格式中的字節(jié)序,例如字節(jié)碼Class文件的字節(jié)序就規(guī)定是大端的院仿。在讀取任何文件時(shí)秸抚,確定其格式(其中包括字節(jié)序),才能正確地解釋歹垫。
對(duì)于第四種剥汤,與第三種是互逆的過(guò)程,寫字節(jié)流時(shí)排惨,需要確定輸出內(nèi)容的格式吭敢,才能正確地輸出結(jié)果。
平臺(tái)的差異確實(shí)會(huì)導(dǎo)致許多意料不到的結(jié)果暮芭,并且需要程序去根據(jù)實(shí)際情況去處理鹿驼,因此一次編譯,到處運(yùn)行的Java辕宏,就彰顯了強(qiáng)大的生命力畜晰,它屏蔽了底層硬件的差異,JVM采用統(tǒng)一的大端字節(jié)序匾效。
位運(yùn)算符的語(yǔ)義
0 1 1 0
1 1 0 0
———
0 1 0 0 & 掩碼舷蟀,被1保留,被0抹掉
1 1 1 0 | 結(jié)合面哼,一人有大家有野宜,否則大家都為0
Java中不同進(jìn)制數(shù)字的表示方法
十進(jìn)制:直接寫 123
十六進(jìn)制:0x前綴 0x7f
八進(jìn)制:0前綴 012
二進(jìn)制:0b前綴 0b1
浮點(diǎn)數(shù)的精度問(wèn)題
IEEE浮點(diǎn)數(shù)標(biāo)準(zhǔn)
V=(-1)^S * M * 2^e
- 符號(hào)(sign):S 浮點(diǎn)數(shù)的正負(fù)
- 階碼(exponent):E
float: e=E?127
double:e=E?1023
浮點(diǎn)數(shù)的階值,表示小數(shù)點(diǎn)右移多少位 - 尾數(shù)(significand):M 浮點(diǎn)數(shù)的數(shù)值魔策,是一個(gè)二進(jìn)制小數(shù)匈子,且整數(shù)部分固定為1,M只存儲(chǔ)小數(shù)部分闯袒,因此可以理解為小數(shù)點(diǎn)在M的最前面虎敦,這樣可以節(jié)省存儲(chǔ)空間
浮點(diǎn)數(shù)格式
Java中浮點(diǎn)數(shù)類型有float和double:
float 32位浮點(diǎn)數(shù):需要加f后綴 1.2f
double 64位浮點(diǎn)數(shù):直接寫或加d后綴 1.2 或 1.2d
例如:?jiǎn)尉雀↑c(diǎn)數(shù) 1.2f
十六進(jìn)制表示為 0x3f99999a
二進(jìn)制表示為 0011 1111 1001 1001 1001 1001 1001 1010
S=0
E=01111111 即127, e=E-127=0
M=1.00110011001100110011010
換算為十進(jìn)制即1.2000000476837158(與原來(lái)的值不一樣了!U摇)
由于1.2轉(zhuǎn)換為二進(jìn)制后小數(shù)部分是無(wú)限循環(huán)小數(shù)其徙,尾數(shù)部分只能做截?cái)嗖⑺纳嵛迦胩幚恚瑢?dǎo)致了精度丟失的問(wèn)題
當(dāng)十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制是出現(xiàn)無(wú)限循環(huán)小數(shù)喷户,或者因小數(shù)部分無(wú)法全部裝入尾數(shù)部分而被截?cái)嗤倌牵紩?huì)出現(xiàn)精度丟失的問(wèn)題。
double比f(wàn)loat的精度更高褪尝,但兩者都會(huì)出現(xiàn)精度丟失闹获。若不能容忍精度丟失期犬,應(yīng)使用BigDecimal進(jìn)行數(shù)字運(yùn)算,并在構(gòu)造函數(shù)時(shí)使用字符串來(lái)傳入數(shù)字避诽。