1. 程式人生 > 其它 >編碼中遇到的這些字串、整數、小數在計算機中是怎麼儲存的?

編碼中遇到的這些字串、整數、小數在計算機中是怎麼儲存的?

引發思考

我用Java語言,如下程式碼,建立了一個int型別、String型別與float型別,計算機存的就是1、“is a String”、3.625麼

int i = 1;
String s = "is a String";
float f = 3.625;

可能我們在想,我不需要知道計算機怎麼存的啊,我會用java的這幾個基礎型別就行了,計算機又不會給我弄錯!

真的不會弄錯麼?

答案是:會,計算機還真的會弄錯,這發生在浮點數的計算過程中,比如下面的計算:
4.0-3.6=?
我們都知道答案是0.4,但是計算機給出的答案可能是:
4.0-3.6=0.40000001
不會吧,計算機這麼“蠢”?我奶奶都會算!(吐槽),哈哈哈,計算機會發生這樣的精度計算差,這是由計算機的儲存機制所導致的計算失誤,要想避免這樣的一個錯誤,我們就需要了解計算機是怎麼儲存一個數又是怎麼參與計算的!

位元組與十六進位制

位元組的由來,大概是1956年前後由IBM公司提出的,用來表示8位二進位制數,為什麼是8位,而不是6位,7位、9位呢?IBM採用位元組是因為它們易於以BCD這種格式儲存,而且用位元組用來表示文字也是合適的,因為8位二進位制數2^8可以代表256種事物中的一個,大多數的書面語言都可以用少於256個字符集來表示。位元組為8位是技術發展過程中擇優選擇的結果。
十六進位制,[1,2,3,4,5,6,7,8,9,A,B,C,D,E,F],它的誕生是為了簡潔明瞭的表示位元組,因為1個8位二進位制數實在太不簡明瞭,例如11100111,10100100,來看看用十六進位制來表示11100111 -> E7

,10100100 -> A4,這就是十六進位制帶來的好處。

儲存數字

"一個數字在計算機中都是以補碼形式儲存的",要想理解這個概念,需要先了解補碼、反碼、原碼是什麼。在Java中我們數值型有四個型別,byte(1個位元組)、short(2個位元組)、int(4個位元組)、long(8個位元組),在計算機中也就是按這些位元組數來儲存的 ,就比如常用的int來說,在計算機中儲存的就是4個位元組32位,比如我們開頭所列的表示式int i = 1,儲存的二進位制為00000000000000000000000000000001,不需要轉換為補碼,因為正整數原碼、反碼、補碼都一樣,這裡可能會問,那麼負數呢?int i = -1呢?負數需要進行補碼轉換,轉換過程為10000000000000000000000000000001(原碼)

-> 11111111111111111111111111111110(反碼) -> 11111111111111111111111111111111(補碼),在計算機中int型的-1儲存的就是11111111111111111111111111111111

儲存文字

我們肯定接觸過ASCII碼字元編碼集。先來理解它的作用,比如我們的一個文字is a String,其中包含了這些字元的i、s、a、S、t、r、n、g、space(空格),ASCII字元編碼集就有一個對應表,對應著i、a等等這些字元對應的十六進位制,我們通過查錶轉換為is a String -> 69(i)、73(s)、20(空格)、61(a)、20(空格)、53(S)、74(t)、72(r)、69(i)、6E(n)、67(g),這就是ASCII字元編碼集的作用,它作為文字資料與二進位制資料中的“翻譯表”。is a String是全英文文字,我們的中文可以通過ASCII來進行“翻譯”嗎?可以,ASCII目前有很多的擴充套件,可以用來表示中文。但是我們常用的還是Unicode編碼集,Unicode採用雙位元組(16位)編碼,ASCII是7位,可以表示2^16=65536個不同字元,Unicode的可擴充套件性更好,給我們預留了很多的空間,對於中文這種複雜一點的語言,有更好的幫助。

儲存小數

小數在計算機中是以浮點數的形式儲存的,浮點數相對於定點數的概念,定點數的含義就是小數點的位置是固定的,比如999999.99與111111.11,都是倒數第三位為小數點位。而浮點數含義為小數點的位置是浮動的,不固定的。比如111111.11、0.0000011等
開頭我們定義了一個單精度浮點數float f = 3.625,我們已知單精度浮點數儲存為4個位元組(可參考IEEE浮點數標準),我們將3.625(十進位制小數)轉換為二進位制小數11.101,轉換過程為整數部分與小數部分單獨轉換,整數部分為3 -> 3/2=1(餘1) -> 1/2=0(餘1),小數部分0.625*2=1.25(取整數部分1) -> 0.25*2=0.5(取0) -> 0.5*2=1(取1),將整數部分結果與小數部分結果整合得出結果為11.101,這就是用二進位制來表示小數數字。那麼在計算機中又是怎麼存這個二進位制的呢?
我們要提到科學記數法,如十進位制中,可以用5.369952314 x 10^5的格式來表示小數,其中首位的5.369952314表示為有效數,10^5中的5表示為指數,在二進位制中與之類似。單精度浮點數包括三部分,如下:

儲存11.101用科學記數法的浮點格式則為11.101-> 1.1101 * 2^1,其中有效數為1101,e-127=1,指數e=128,符號位為0(因為為正浮點數),拼接起來則為0(佔1位) 10000000(佔8位) 00000000000000000001101(23位有效數),這就是計算機中儲存單精度浮點數3.14的最終二進位制格式了。
回到開始,我們發現計算機計算4.0 - 3.6 = 0.40000001時有錯誤,這是怎麼造成的呢?
4.0轉成浮點數為0 10000001 00000000000000000000000,3.6轉成浮點數為3.6 -> 11.1001 1001 1001 1001 1001 1001...,3.6轉浮點數,小數位無限迴圈,不能取盡,但是單精度浮點數與雙精度浮點數都是有精度範圍的。只能去精度範圍內的資料。3.6=0 10000000 11001100110011001100110(單精度有效位為23位),將4.0與3.6相減(浮點數運算)後的結果為0.01100110011001100110011,我們來看下0.40000001的二進位制表示:

我們發現與4.0-3.6運算結果相同,這就是計算機中浮點數運算導致的精度誤差。不僅僅是4.0-3.6,在浮點數計算中,會經常發現這種問題。

總結

1、計算機中儲存任何資料都用的是二進位制儲存,且以位元組為儲存單位。
2、計算機中儲存數字時,用補碼來表示負數。
3、計算機中儲存文字資料,是有一個文字字元與位元組對映表,比如ASCII、Unicode等
4、計算機中儲存小數用的是浮點數格式,結合科學記數法。且浮點數運算時會產生一些精度問題,用double雙精度會避免一些精度錯誤,但是不能完全解決問題。電腦科學家們用了一個演算法來解決,Kahan Summation,此演算法原理就是在每次的計算過程中,都用一次減法,把當前加法計算中損失的精度記錄下來,然後在後面的迴圈中,把這個精度損失放在要加的小數上,再做一次運算。