浮點數與整型數的"互轉"


看了標題,你是不是覺得這TM是哪個iOS彩筆寫的入門文章.好的,那咱們先來看看幾個例題,看看你有沒有白白點進來!

int main() {
    float a = -6.0;
    int *b = &a;
    NSLog(@"1=> %d",*b);

    *b = -6;
    NSLog(@"2=> %f",a);
    return 0;
}

請問上面的1、2分別輸出什么?(趕緊拿出草稿紙算一算吧!)

什么,這么簡單的題目還要拿出草稿紙算,你TM是在侮辱我的智商嗎?
輸出結果不就是這個嗎?

1=> -6
2=> -6.000000

還說不是彩筆寫的文章,TM騙流量!

別急着返回,往下看!
首先為了防止你直接走了,那么我先拋出輸出結果!看好了,千萬別眨眼:

輸出結果:
1=> -1061158912
2=> nan

對,沒有看錯,這就是輸出結果.

我知道,有些人還不信,如果你確實不相信,而且你旁邊有電腦,代碼拷進Xcode跑一下,看看是這結果嗎?(跑完之后再回來看,沒錯,相信我).

好的,跑完代碼,你就很想往下看了吧,那就讓我們一起來分析下,怎么會有這么魔性的結果,這不科學啊!!!!


首先,問題的關鍵在於不同類型的指針,指向相通的數據.

那么數據在計算機中是如何保存的呢?當然是二進制啊,就是一堆0和1.但是0和1只有計算機能懂啊,我們程序員哪里知道,一坨0和1表示什么呢!所以,計算機就要將保存在內存當中的0和1轉換為我們能看得懂的數據,數字當然就被轉換為了10進制的.這里有個關鍵點: 二進制數轉換為十進制數.

這里抽離出了兩個關鍵點 :

** 1. 整型數據 <==> 浮點型數據 **

**2. 進制 <==> 十進制 **

那么數據在內存中具體的保存方式是什么呢?

整型數據 4個字節 32位 每一位都是0或1,首位代表符號位.首位1代表負數;首位0,代表正數;

如: 5 =(轉換為2進制)= 0 00..(中間略掉24個0)..00101 但是負數,保存的就是他的補碼了. 補碼 = 反碼 + 1; 比如: -5 的 補碼計算方式: 5 =(轉為二進制)=> 0 00..(中間略掉24個0)..00101 =(求反碼:0變為1,1變為0)=> 1 11..(中間略掉24個1)..11010 =(補碼:反碼 + 1)=> 1 11..(中間略掉24個1)..11011 

好的,比較簡單是吧!

那我們再看看浮點型數據的保存方式吧:

首先有一個公式,來將浮點型數據轉化為我們好保存的結果,公式如下:

V = (-1)^S * M * 2^E;

  1. S: 符號位(Sign) : (-1)^s表示符號位,當s=0,V為正數;當s=1,V為負數;
  2. E: 指數位(Exponent): M表示有效數字,大於等於1,小於2;
  3. M: 尾數部分(Mantissa): 2^E表示指數位.

指數E還可以再分成三種情況:

  1. E不全為0或不全為1.這時,浮點數就采用上面的規則表示,即指數E的計算值減去127(或1023),得到真實值,再將有效數字M前加上第一位的1.
  2. E全為0.這時,浮點數的指數E等於1-127(或者1-1023),有效數字M不再加上第一位的1,而是還原為0.xxxxxx的小數.這樣做是為了表示±0,以及接近於0的很小的數字.
  3. E全為1.這時,如果有效數字M全為0,表示±無窮大(正負取決於符號位s);如果有效數字M不全為0,表示這個數不是一個數(NaN).

比如:

6.0 =(二進制)=> 110.0 =(按上式轉換)=> (-1)^0 * 1.1 * 2^2 => S = 0;M = 1.1; E = 2; 

那么有了公式,又有什么用呢?這個公式其實完全告訴我們浮點數據的保存方式了.

具體是這樣的,在32位的二進制數據中,第1位保存S,接下來8未保存E,剩下的保存M.

注意,為了能讓E表示負數,在保存的時候,會將它 +127,在取的時候,又撿回去,這樣就能表示負數了;由於M都是1.,所以就不保存前面的1了,在取的時候再拿回去.*

說這么多,還是煮個🌰吧,還是上面的6.0:

6.0 =(按公式轉換)=> (-1)^0 * 1.1 * 2^2 => S = 0;M = 1.1; E = 2; 那么保存方式就是: [S(符號1位) E(指數8位: +127之后再保存) M(位數23位:去掉首位的1再保存)] =>[0 129 1] =>[0 10000001 1000..(中間略掉17個0)..00] 

如果把他當整型取出來會是什么結果呢?

那就是: 2^30 + 2^23 + 2^22 = 1086324736; (可以自己敲代碼驗證一下,當然也可以筆算) 

既然知道了數據在內存中的存儲方式了,那不就結了.

有了刀,那就可以開始切菜了!!!

第一個問題就是: 將 -6.0的浮點數當整型數取出來,結果是什么?
首先套公式 : -6.0 = -110.0 = (-1)^1 * 1.1 * 2 ^2 => S = 1;M = 1.1;E = 2; 那么它在內存中的形式就是: [S(符號1位) E(指數8位: +127之后再保存) M(位數23位:去掉首位的1再保存)] =>[1 129 1] =>[1 10000001 1000..(中間略掉17個0)..00] 

接着我們將它當做整型數取出來.首先看到,符號位為1,那么是負數.回憶一下負整型數是怎么保存在內存當中的呢?對,保存 補碼.補碼是反碼+1.那我們現在把它發過來做就可以拿到原碼了.即-1,然后取反碼.

[1 10000001 1000....00] =(-1)=> [1 10000001 0111..(中間略掉17個1)..11] =(取反碼)=>[0 01111110 1000..(中間略掉17個0)..00] 結果就是: -(2^29 + 2^28 + 2^27 + 2^26 + 2^25 + 2^24 + 2^22) = -1061158912; 

是不是感覺很爽,我他媽竟然把所謂的"bug""的結果算出來了.不要太激動,我們繼續,第二個問題:

第二個問題是:把整型的-6當浮點型取出來.

好的,負整型保存補碼;

6 =(2進制)=> [00..(中間略掉25個0)..00 110] =(反碼)=> [11..(中間略掉25個1)..11 001] =(+1)=> [11..(中間略掉25個1)..11 010] 

可以看到,他的指數位全部為1.我們再回頭看看 指數E的三種情況.E全為1,這時,如果有效數字M全為0,表示±無窮大(正負取決於符號位s);如果有效數字M不全為0,表示這個數不是一個數(NaN:not a number).

所以最終的打印結果為 nan

One More Thing

可能對於科班出身的iOS程序員而言,這些東西,確實是彩筆才寫的問題.但是,像我這種半路出家的人來說,路還很遠!



作者:Ljson
鏈接:https://www.jianshu.com/p/810f89d97421
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM