您好,登錄后才能下訂單哦!
這篇文章主要為大家分析了iOS浮點類型精度問題的原因與解決辦法是什么的相關知識點,內容詳細易懂,操作細節合理,具有一定參考價值。如果感興趣的話,不妨跟著跟隨小編一起來看看,下面跟著小編一起深入學習“iOS浮點類型精度問題的原因與解決辦法是什么”的知識吧。
相信不少人(其實我覺得應該是每個人)都遇到過一個問題,那就是當服務端返回的JSON數據中出現了小數時,客戶端用CGFloat去解析時總是會出現精度丟失的問題,尤其當遇到敏感數據時,這種精度丟失是完全不能被容忍的,下面會從簡單的解決方案和原理出發,一起帶大家回顧一下這個其實大家以前都學過但是都忘的差不多了的小問題。
//例如服務端返回了這么一個json { "price":1.9 } // 客戶端解析price后并且打印1.9 CGFloat price = model.price NSLog(@"%f",price) 得到的結果是 1.8999999999999999
這個時候呢,作為一個咸魚開發者,可以寫下如下代碼:
NSLog(@"%.2f",price) 輸出:1.89 //不夠準確,沒關系,還有辦法 NSLog(@"f%@",round(price*10)/10); 輸出1.9
當然了,還有apple專門為精度問題提供的 NSDecimalNumber類型也可以解決這個問題。NSDecimalNumber的用法非常簡單。(至于怎么個簡單,請自行百度,別問我,我不百度也寫不出來)
那么問題來了,作為一個懶到一整年都不愿意寫文章的咸魚中的咸魚,我應該選擇哪種方式去解決這個問題呢?
好的,重頭戲來了,接下來,我們上代碼!
//服務端必須返回字符串類型,如果服務端沒有照做: **請帶著錘子和煤氣罐去找后端開發人員解決** @property (nonatomic,copy) NSString *price;
是的,我會選擇不解決這個問題,把皮球踢出去,這才是一個合格的開發者該做的事情嘛!
解決浮點精度問題是一個方面,但是如此簡單的內容不足以我水完一整篇文章,那么接下來,我們來講講為什么好好的數字解析出來,他咋就不準確了呢?
可能很多人都能大概講出來精度丟失是因為浮點數存儲方式的問題,畢竟這玩意兒其實專業對口的筒子們在校的時候都學過,但是大家摸摸自己的小腦袋,嘿,是不是和我一樣?全忘光了?
而且鑒于總有些基礎很牛逼的面試官和剛好復習過這部分內容的裝逼面試官就是喜歡挑這些小問題來刁難咱們這些老年摸魚程序員,下面我們就來復習一下這部分的知識吧。
浮點類型在計算機中的存儲方式是以科學計數法的方式來存儲的:
例如: 科學計數法表示小數 90.9 => 9.09 x 10^1 8.3 => 8.3 x 10^0
我們以8.3為例子,要存儲8.3 (8.3 x 10^0), 首先肯定要將8.3轉化為2進制,8轉為二進制時1000,那么0.3呢?
年邁的程序員喲,你是不是猛然發現居然忘了小數是如何轉化為2進制的呀,放下你準備百度的顫抖的雙手,你想要的,我這里都有!
以0.9為例子,將0.9乘以2,得到的數字整數部分為二進制的小數第一位,將結果部分的小數部分取出并再乘以2,取整數部分為第2位,不斷重復以上操作,直到結果等于0或者出現循環為止。
0.9 *2 = 1.8 第1位 1 0.8 *2 = 1.6 第2位 1 0.6 *2 = 1.2 第3位 1 0.2 *2 = 0.4 第4位 0 0.4 *2 = 0.8 第5位 0 0.8 *2 = 1.6 第6位 1 出現循環了 那么0.9的二進制就是 0.1 1100 1100 1100 1100... 其中1100無限循環
經過上面一番復習我們知道8.3的二進制可表示為 1000.0 1001 1001 1001... (1001無限循環) 用科學計數法表示則為, 1.000 0 1001 1001 1001 x 2^3
整數部分 | 指數部分 | 小數部分 |
---|---|---|
1 | 3 | .000 0 1001 1001 1001 |
而我們知道float是4(32位)個字節,在存儲時他的每個bit是如下分配的
第31位符號位 | 23-30位(指數位) | 0-22位(小數位) |
---|---|---|
1 | 3 | .000 0 1001 1001 1001 |
可以看到尾數部分有23位,但是由于是科學技術法之后任何一個數字都可以用 1.aaa x 2^b 這種形式來表示,所以1可以省略,(這個時候就有人要抬杠了,為啥,那遇到0.aaa x 2^b這種咋辦呀! 答曰:b可以是負數啊),那么總共23位的數據實際可以表達的位數其實就是24位。
24位可以表達的最大數字是 16777215 ,如果大于這個數就無法精確表示了。
當然大于16777215一不定是完全不能精確表示的,比如16777216,他的二進制表達形式是1后面帶1堆0. 因為他是2的整數次冪,那么后面全是0 所以23位能把該數字存儲下來,如果在23位0后面還出現了1就不行了,以此類推,16777215 后面還會有數字剛好可以滿足這種2的整數次冪的條件,也可以正確表達。
雖然大于16777215的數字也有部分可以精確表達,但是我們談精度的話肯定就要精確了,那么能精確表達的數字就只有0-16777215之間的了,16777215之間我們數一下,一共是8位,但是由于最高位1開頭并不能全部包含,所以說精度應該是7位有效數子。
另外提一下浮點數的表達范圍,這個范圍肯定是由指數來確定了,具體是多少我就不算了,大家有興趣的可以去算一算。
可以看到指數部分一共是給了8個bit位,由于指數有正負,那么假設我們第一位表示符號位,那么我們可以表示的數字范圍為 -127 ~ +127
那么能表示的范圍被分為兩部分:
1 000000 0~ 1 111111 1 => -0 到 -127
0 000000 0~ 0 111111 1 => +0 到 127
很明顯,如果這樣會出現一個 -0 和一個 +0,為了避免這個問題所以出現了移位存儲:即如果最高位不用來表示符號位,8個bit 可以存儲的范圍是 0-255,我們重新來規劃下兩個區間:
00000000 - 01111111 => 0-127 減去127則表示-127-0之前的數字
10000000 - 11111111 => 128-256 減去127則表示1-127之前的數字
這樣一來規避了+-0的問題, 上文中所說的8.3 轉化為小數后的指數位是3,那么3實際存儲的時130,也就是 10000010,我們更新一下實際存儲的表格
第31位符號位 | 23-30位(指數位) | 0-22位(小數位) |
---|---|---|
1 | 10000010 | .000 0 1001 1001 1001 |
double類型是8字節64位,其表示的位數和float所不同只有位數差別,其他都一樣,雙精度表示的位數如下:
第63位符號位 | 52-62位(指數位) | 0-51位(小數位) |
---|
回到最初的1.9這個數字打印為1.8999999999999999,現在我們應該知道為什么了,因為1.9轉2進制時是個無限循環,由于存儲的原因后面的循環部分被只被截取了23位,還原回來的十進制數字和原先肯定就不一樣了。 同理,如果小數點后面是.5這種5的倍數的小數,打印出來的小數一般都會是正常的,因為.5轉2進制時并不是無限循環的。
關于“iOS浮點類型精度問題的原因與解決辦法是什么”就介紹到這了,更多相關內容可以搜索億速云以前的文章,希望能夠幫助大家答疑解惑,請多多支持億速云網站!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。