關(guān)于浮點型精度問題的一些理解

這是一篇關(guān)于浮點型精度的文章业踢,大致會從三個大的問題入手,去解析精度問題谨履。

字符串轉(zhuǎn)浮點型出現(xiàn)精度丟失

浮點型和浮點型操作精度丟失

NSDecimalNumber如何解決精度丟失

前言

4月21號終于完成了4月份的自考革为,也有些時間來研究一些東西,寫寫博客了糕簿,剛好群里邊有人問關(guān)于浮點型精度的問題探入,我去研究了一些資料,問了一些前輩和同行懂诗,總結(jié)如下新症,結(jié)尾處會有我參考資料的鏈接。(以下內(nèi)容均是基于32位系統(tǒng)進(jìn)行描述的)

字符串在內(nèi)存中的存儲方式

首先响禽,我們了解一下字符串的存儲方式徒爹,它是以ASCII碼進(jìn)行存儲的,然后將對應(yīng)的ASCII碼轉(zhuǎn)換成二進(jìn)制存儲在內(nèi)存中芋类。

例如:a對應(yīng)的ASCII碼是97隆嗅,97對應(yīng)的七位二進(jìn)制表示是1100001,八位是01100001侯繁,這兩者區(qū)別可以忽略掉胖喳,八位二進(jìn)制可表示的字符范圍更寬泛而已。因為是一個字符所以只需要1個字節(jié)贮竟,也就是8位丽焊,也就是1組二進(jìn)制。

1.23作為一個字符串它由4個字符組成所以需要4個字節(jié)咕别,也就是32位技健,需要4組二進(jìn)制。它們分別對應(yīng)的ASCII碼是49惰拱、46雌贱、50、51,轉(zhuǎn)換成二進(jìn)制就分別是110001欣孤、101110馋没、110010、110011降传。

浮點型在內(nèi)存中的存儲方式

浮點型作為一種與整型不同的存儲方式篷朵,它是遵循IEEE754標(biāo)準(zhǔn)的。浮點型在內(nèi)存中的存儲分為了3個部分婆排,分別是1個符號位s声旺,8個指數(shù)E,23個有效數(shù)字M泽论。任意一個二進(jìn)制數(shù)V都可以寫成sM*2^E,s就是正號和負(fù)號卡乾,M就是有效數(shù)字翼悴,E就是指數(shù)。這個公式的意思是將M的小數(shù)點向右移E位幔妨。下面是IEEE754的一些規(guī)定:

1

2

3

4

5

1. 當(dāng)s=0鹦赎,V為正數(shù);當(dāng)s=1误堡,V為負(fù)數(shù)古话。

2. M表示有效數(shù)字,大于等于1锁施,小于2陪踩。

3. 2^E表示指數(shù)位。

4. E的真實值必須再減去一個中間數(shù)悉抵,對于8位的E肩狂,這個中間數(shù)是127。

5. 在計算機(jī)內(nèi)部保存M時姥饰,默認(rèn)這個數(shù)的第一位總是1傻谁,因此可以被舍去,只保存后面的xxxxxx部分列粪。

例如浮點數(shù)10.0审磁,轉(zhuǎn)換成二進(jìn)制是1010,在32位系統(tǒng)下你還要在前面補上28個0岂座,這個二進(jìn)制并不是在內(nèi)存中的存儲樣式态蒂。套用上面的公式你可以寫成+1.010*2^3,其中s是0费什,那么也就是+吃媒,M就是1.01,E就是3。這只是公式赘那,并不是在內(nèi)存中的真正存儲形式刑桑。接下來看下下面的圖,圖中的數(shù)值不用看募舟,跟本文無關(guān)祠斧,主要是看組成的三部分。


看過了上圖拱礁,還是以+1.010*2^3為例琢锋,將其填入該32位的二進(jìn)制中。

首先是符號位S呢灶,也就是圖中的sign吴超,因為是正號,所以sign是0鸯乃;

E是3鲸阻,按第4條規(guī)則,E的真實值需要E+127缨睡,也就是130鸟悴,將其轉(zhuǎn)成二進(jìn)制存儲,也就是10000010奖年;

M是1.010细诸,按上述第5條規(guī)則,舍去1陋守,也就是01000000000000000000000震贵;

所以合在一起,浮點數(shù)10.0在內(nèi)存中的表示就是01000001001000000000000000000000水评。

整型在內(nèi)存中的存儲方式

既然浮點型和字符串在內(nèi)存中的存儲方式都說了屏歹,順道提下整型在內(nèi)存中的存儲方式。

這個很簡單之碗,它只需要考慮一個問題蝙眶,就是正數(shù)和負(fù)數(shù),它由兩部分構(gòu)成褪那,第一個還是符號位幽纷,表示正負(fù),后面31位都是實際存儲的數(shù)字博敬,所以它支持存儲的數(shù)字范圍是-2^31~~~2^31-1友浸。只需要直接將整數(shù)轉(zhuǎn)換成二進(jìn)制就可以了。

比如100偏窝,在內(nèi)存中的二進(jìn)制表示就是00000000000000000000000001100100收恢。

字符串轉(zhuǎn)浮點型出現(xiàn)精度丟失

前面說了字符串的存儲方式和浮點型的存儲方式武学,現(xiàn)在這個問題其實挺好解決的了。對了伦意,還要說的一點是火窒,你用的什么方法進(jìn)行強轉(zhuǎn)的,對于NSString類型的轉(zhuǎn)成float驮肉,一般使用的是floatValue方法熏矿,那么可以看下官方文檔對這個方法的解讀。

1

2

3

The floating-point value of the string as a float.

This property doesn’t include whitespace at the beginning of the string. This property is HUGE_VAL or –HUGE_VAL on overflow, 0.0 on underflow. This property is 0.0 if the string doesn’t begin with a valid text representation of a floating-point number.

This method uses formatting information stored in the non-localized value; use an NSScanner object for localized scanning of numeric values from a string.

上文比較有用的信息就是這個方法是通過NSScanner對字符串進(jìn)行逐個掃描离钝,如果不是一個真正的浮點型票编,比如@”abv”這種,這個方法就是0.0卵渴;如果是@”1.23”這種慧域,它就會轉(zhuǎn)化成浮點型1.23。所以可以排除掉這個可能:浮點型和字符串在內(nèi)存中的二進(jìn)制表現(xiàn)形式不同而導(dǎo)致的浪读。

那么昔榴,問題就很清楚了,肯定是浮點型自身存儲成2進(jìn)制的時候發(fā)生了精度丟失瑟啃。這次舉兩個例子對比下:

浮點數(shù)10.0论泛,它的有效數(shù)字M是1.010揩尸,忽略掉整數(shù)位1蛹屿,實際存儲的也就3位是010,在32位情況下岩榆,M最多可以存儲23位有效數(shù)错负,所以它是無損的。

浮點數(shù)1.2勇边,它的有效數(shù)字是0011001100110011001100110011001100110011001100110011犹撒,這個長度大大超過了23位能存儲的,所以它會被截取掉后面超出的部分粒褒,超出部分明顯不全是0识颊,所以會對它的精度造成損失。

總結(jié):精度損失不損失需要看十進(jìn)制的數(shù)據(jù)能否精確的轉(zhuǎn)換為二進(jìn)制奕坟。

浮點型和浮點型操作精度丟失

關(guān)于這個問題祥款,其實看懂了上面的內(nèi)容,就知道這個問題出在哪里了月杉。比如a=b+c;首先浮點型b和c自身存儲就已經(jīng)損失精度了刃跛,其次得到的結(jié)果a如果也是一個不能精確轉(zhuǎn)為二進(jìn)制的浮點型,那么必然造成精度的2次缺失苛萎,會跟你想象中的結(jié)果差距更大桨昙。

NSDecimalNumber如何解決精度丟失

找到了問題的產(chǎn)生原因检号,再來說說iOS提供的NSDecimalNumber這個類如何解決這個問題。我們先來看下系統(tǒng)文檔:

1NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.

上述大概意思是NSDecimalNumber是NSNumber的一個子類蛙酪,它提供了一個基于10進(jìn)制面向?qū)ο蟮姆庋b齐苛。它也有一個類似IEEE754的公式:N*M*10^E

例如浮點型1.23,套用上面公式就是1*123*10^(-2)滤否。它會將這個浮點型包裝成一個NSDecimalNumber對象脸狸,N代表是正數(shù)還是負(fù)數(shù),在本例中是1藐俺,M代表將浮點型轉(zhuǎn)化為整數(shù)后的數(shù)炊甲,在本例中是123,E代表指數(shù)欲芹,在本例中是-2卿啡。

這樣來看,系統(tǒng)的處理方式其實很明顯了菱父,這個類不存儲浮點型颈娜,只存儲整數(shù),自然避免了IEEE754那種方式的精度損失浙宜,當(dāng)然類在內(nèi)存中肯定跟浮點型是不同的官辽,但是M這個屬性保存的是整數(shù)型,是同浮點型一樣都是基礎(chǔ)數(shù)據(jù)類型粟瞬。

后續(xù)

其實還有一個整型強轉(zhuǎn)浮點型的問題同仆,這個問題跟字符串轉(zhuǎn)浮點型的情況還是不同的,這個是因為兩者對二進(jìn)制的轉(zhuǎn)化方式不同裙品。整型存儲在內(nèi)存中是直接轉(zhuǎn)化成二進(jìn)制的俗批,浮點型雖然也是轉(zhuǎn)成二進(jìn)制存儲,但它需要符合IEEE754標(biāo)準(zhǔn)市怎,所以你直接把整型的二進(jìn)制取出來當(dāng)浮點型使用岁忘,肯定會出現(xiàn)對應(yīng)的問題。

原文鏈接: 關(guān)于浮點型精度問題的一些理解 | YownYang's blog

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末区匠,一起剝皮案震驚了整個濱河市干像,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驰弄,老刑警劉巖麻汰,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異揩懒,居然都是意外死亡什乙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門已球,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臣镣,“玉大人辅愿,你說我怎么就攤上這事∫淠常” “怎么了点待?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弃舒。 經(jīng)常有香客問我癞埠,道長,這世上最難降的妖魔是什么聋呢? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任苗踪,我火速辦了婚禮,結(jié)果婚禮上削锰,老公的妹妹穿的比我還像新娘通铲。我一直安慰自己,他們只是感情好器贩,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布颅夺。 她就那樣靜靜地躺著,像睡著了一般蛹稍。 火紅的嫁衣襯著肌膚如雪吧黄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天唆姐,我揣著相機(jī)與錄音拗慨,去河邊找鬼。 笑死厦酬,一個胖子當(dāng)著我的面吹牛胆描,可吹牛的內(nèi)容都是我干的瘫想。 我是一名探鬼主播仗阅,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼国夜!你這毒婦竟也來了减噪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤车吹,失蹤者是張志新(化名)和其女友劉穎筹裕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窄驹,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡朝卒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乐埠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抗斤。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡囚企,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瑞眼,到底是詐尸還是另有隱情龙宏,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布伤疙,位于F島的核電站银酗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏徒像。R本人自食惡果不足惜黍特,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锯蛀。 院中可真熱鬧衅澈,春花似錦、人聲如沸谬墙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拭抬。三九已至部默,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間造虎,已是汗流浹背傅蹂。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留算凿,地道東北人份蝴。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像氓轰,于是被迫代替她去往敵國和親婚夫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容