小數(shù)的計(jì)算

續(xù)上期《如何操作數(shù)據(jù)》本文和大家一起學(xué)習(xí)小數(shù)的計(jì)算

違反直覺(jué)的事實(shí)
計(jì)算機(jī)之所以叫"計(jì)算"機(jī)就是因?yàn)榘l(fā)明它主要是用來(lái)計(jì)算的辆沦,"計(jì)算"當(dāng)然是它的特長(zhǎng),在大家的印象中挤土,計(jì)算一定是非常準(zhǔn)確的琴庵。但實(shí)際上,即使在一些非逞雒溃基本的小數(shù)運(yùn)算中迷殿,計(jì)算的結(jié)果也是不精確的。

比如:

float f = 0.1f*0.1f;
System.out.println(f);

這個(gè)結(jié)果看上去筒占,不言而喻贪庙,應(yīng)該是0.01,但實(shí)際上翰苫,屏幕輸出卻是0.010000001止邮,后面多了個(gè)1。

看上去這么簡(jiǎn)單的運(yùn)算奏窑,計(jì)算機(jī)怎么會(huì)出錯(cuò)了呢导披?

簡(jiǎn)要答案
實(shí)際上,不是運(yùn)算本身會(huì)出錯(cuò)埃唯,而是計(jì)算機(jī)根本就不能精確的表示很多數(shù)撩匕,比如0.1這個(gè)數(shù)。

計(jì)算機(jī)是用一種二進(jìn)制格式存儲(chǔ)小數(shù)的墨叛,這個(gè)二進(jìn)制格式不能精確表示0.1止毕,它只能表示一個(gè)非常接近0.1但又不等于0.1的一個(gè)數(shù)。

數(shù)字都不能精確表示漠趁,在不精確數(shù)字上的運(yùn)算結(jié)果不精確也就不足為奇了扁凛。

0.1怎么會(huì)不能精確表示呢?在十進(jìn)制的世界里是可以的闯传,但在二進(jìn)制的世界里不行谨朝。在說(shuō)二進(jìn)制之前,我們先來(lái)看下熟悉的十進(jìn)制。

實(shí)際上字币,十進(jìn)制也只能表示那些可以表述為10的多少次方和的數(shù)则披,比如12.345,實(shí)際上表示的:110+21+30.1+40.01+5*0.001洗出,與整數(shù)的表示類(lèi)似士复,小數(shù)點(diǎn)后面的每個(gè)位置也都有一個(gè)位權(quán),從左到右共苛,依次為 0.1,0.01,0.001,...即10^(-1), 10^(-2), 10^(-3)判没。

很多數(shù),十進(jìn)制也是不能精確表示的隅茎,比如1/3, 保留三位小數(shù)的話(huà)澄峰,十進(jìn)制表示是0.333,但無(wú)論后面保留多少位小數(shù)辟犀,都是不精確的俏竞,用0.333進(jìn)行運(yùn)算,比如乘以3堂竟,期望結(jié)果是1魂毁,但實(shí)際上卻是0.999。

二進(jìn)制是類(lèi)似的出嘹,但二進(jìn)制只能表示哪些可以表述為2的多少次方和的數(shù)席楚,來(lái)看下2的次方的一些例子:


image.png

可以精確表示為2的某次方之和的數(shù)可以精確表示,其他數(shù)則不能精確表示税稼。

為什么一定要用二進(jìn)制呢烦秩?
為什么就不能用我們熟悉的十進(jìn)制呢?在最最底層郎仆,計(jì)算機(jī)使用的電子元器件只能表示兩個(gè)狀態(tài)只祠,通常是低壓和高壓,對(duì)應(yīng)0和1扰肌,使用二進(jìn)制容易基于這些電子器件構(gòu)建硬件設(shè)備和進(jìn)行運(yùn)算抛寝。如果非要使用十進(jìn)制,則這些硬件就會(huì)復(fù)雜很多曙旭,并且效率低下盗舰。

有什么有的小數(shù)計(jì)算是準(zhǔn)確的
如果你編寫(xiě)程序進(jìn)行試驗(yàn),你會(huì)發(fā)現(xiàn)有的計(jì)算結(jié)果是準(zhǔn)確的桂躏。比如钻趋,我用Java寫(xiě):

System.out.println(0.1f+0.1f);  
System.out.println(0.1f*0.1f);

第一行輸出0.2,第二行輸出0.010000001沼头。按照上面的說(shuō)法爷绘,第一行的結(jié)果應(yīng)該也不對(duì)啊进倍?

其實(shí)土至,這只是Java語(yǔ)言給我們?cè)斐傻募傧螅?jì)算結(jié)果其實(shí)也是不精確的猾昆,但是由于結(jié)果和0.2足夠接近陶因,在輸出的時(shí)候,Java選擇了輸出0.2這個(gè)看上去非常精簡(jiǎn)的數(shù)字垂蜗,而不是一個(gè)中間有很多0的小數(shù)楷扬。

在誤差足夠小的時(shí)候,結(jié)果看上去是精確的贴见,但不精確其實(shí)才是常態(tài)烘苹。

怎么處理計(jì)算不精確
計(jì)算不精確,怎么辦呢片部?大部分情況下镣衡,我們不需要那么高的精度,可以四舍五入档悠,或者在輸出的時(shí)候只保留固定個(gè)數(shù)的小數(shù)位廊鸥。

如果真的需要比較高的精度,一種方法是將小數(shù)轉(zhuǎn)化為整數(shù)進(jìn)行運(yùn)算辖所,運(yùn)算結(jié)束后再轉(zhuǎn)化為小數(shù)惰说,另外的方法一般是使用十進(jìn)制的數(shù)據(jù)類(lèi)型,這個(gè)沒(méi)有統(tǒng)一的規(guī)范缘回,在Java中是BigDecimal吆视,運(yùn)算更準(zhǔn)確,但效率比較低切诀,本節(jié)就不詳細(xì)說(shuō)了揩环。

二進(jìn)制表示
我們之前一直在用"小數(shù)"這個(gè)詞表示float和double類(lèi)型,其實(shí)幅虑,這是不嚴(yán)謹(jǐn)?shù)模?小數(shù)"是在數(shù)學(xué)中用的詞丰滑,在計(jì)算機(jī)中,我們一般說(shuō)的是"浮點(diǎn)數(shù)"倒庵。float和double被稱(chēng)為浮點(diǎn)數(shù)據(jù)類(lèi)型褒墨,小數(shù)運(yùn)算被稱(chēng)為浮點(diǎn)運(yùn)算。

為什么要叫浮點(diǎn)數(shù)呢擎宝?這是由于小數(shù)的二進(jìn)制表示中郁妈,表示那個(gè)小數(shù)點(diǎn)的時(shí)候,點(diǎn)不是固定的绍申,而是浮動(dòng)的噩咪。

我們還是用10進(jìn)制類(lèi)比顾彰,10進(jìn)制有科學(xué)表示法,比如123.45這個(gè)數(shù)胃碾,直接這么寫(xiě)涨享,就是固定表示法,如果用科學(xué)表示法仆百,在小數(shù)點(diǎn)前只保留一位數(shù)字厕隧,可以寫(xiě)為1.2345E2即1.2345*(10^2),即在科學(xué)表示法中俄周,小數(shù)點(diǎn)向左浮動(dòng)了兩位吁讨。

二進(jìn)制中為表示小數(shù),也采用類(lèi)似的科學(xué)表示法峦朗,形如 m*(2^e)建丧。m稱(chēng)為尾數(shù),e稱(chēng)為指數(shù)波势。指數(shù)可以為真茶鹃,也可以為負(fù),負(fù)的指數(shù)表示哪些接近0的比較小的數(shù)艰亮。在二進(jìn)制中闭翩,單獨(dú)表示尾數(shù)部分和指數(shù)部分,另外還有一個(gè)符號(hào)位表示正負(fù)迄埃。

幾乎所有的硬件和編程語(yǔ)言表示小數(shù)的二進(jìn)制格式都是一樣的疗韵,這種格式是一個(gè)標(biāo)準(zhǔn),叫做IEEE 754標(biāo)準(zhǔn)侄非,它定義了兩種格式蕉汪,一種是32位的,對(duì)應(yīng)于Java的float逞怨,另一種是64位的者疤,對(duì)應(yīng)于Java的double。

32位格式中叠赦,1位表示符號(hào)驹马,23位表示尾數(shù),8位表示指數(shù)除秀。64位格式中糯累,1位表示符號(hào),52位表示尾數(shù)册踩,11位表示指數(shù)泳姐。

在兩種格式中,除了表示正常的數(shù)暂吉,標(biāo)準(zhǔn)還規(guī)定了一些特殊的二進(jìn)制形式表示一些特殊的值胖秒,比如負(fù)無(wú)窮缎患,正無(wú)窮,0阎肝,NaN (非數(shù)值较锡,比如0乘以無(wú)窮大)。

IEEE 754標(biāo)準(zhǔn)有一些復(fù)雜的細(xì)節(jié)盗痒,初次看上去難以理解,對(duì)于日常應(yīng)用也不常用低散,本文就不介紹了俯邓。

如果你想查看浮點(diǎn)數(shù)的具體二進(jìn)制形式,在Java中熔号,可以使用如下代碼:

Integer.toBinaryString(Float.floatToIntBits(value))
Long.toBinaryString(Double.doubleToLongBits(value));

總結(jié)

  • 小數(shù)計(jì)算為什么會(huì)出錯(cuò)呢稽鞭?理由就是:很多小數(shù)計(jì)算機(jī)中不能精確表示。
  • 計(jì)算機(jī)的基本思維是二進(jìn)制的引镊,所以朦蕴,意料之外,情理之中!

整數(shù)的二進(jìn)制和小數(shù)的二進(jìn)制我們都做了一定的分析學(xué)習(xí)弟头,那字符和文本呢吩抓?編碼是怎么回事?亂碼又是什么原因赴恨?下期繼續(xù)更新……關(guān)注


今天正值正月十五中國(guó)傳統(tǒng)節(jié)日元宵節(jié)疹娶,筆者在這里祝愿大家身體健康、萬(wàn)事如意伦连,大家元宵節(jié)快樂(lè)雨饺!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惑淳,隨后出現(xiàn)的幾起案子额港,更是在濱河造成了極大的恐慌,老刑警劉巖歧焦,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件移斩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绢馍,警方通過(guò)查閱死者的電腦和手機(jī)叹哭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痕貌,“玉大人风罩,你說(shuō)我怎么就攤上這事钻心【龀蓿” “怎么了区匠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵九巡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我室琢,道長(zhǎng)乾闰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任盈滴,我火速辦了婚禮涯肩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巢钓。我一直安慰自己病苗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布症汹。 她就那樣靜靜地躺著硫朦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪背镇。 梳的紋絲不亂的頭發(fā)上咬展,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音瞒斩,去河邊找鬼破婆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胸囱,可吹牛的內(nèi)容都是我干的荠割。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旺矾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔑鹦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起箕宙,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嚎朽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后柬帕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哟忍,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年陷寝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锅很。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凤跑,死狀恐怖爆安,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仔引,我是刑警寧澤扔仓,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布褐奥,位于F島的核電站,受9級(jí)特大地震影響翘簇,放射性物質(zhì)發(fā)生泄漏撬码。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一版保、第九天 我趴在偏房一處隱蔽的房頂上張望呜笑。 院中可真熱鬧,春花似錦彻犁、人聲如沸叫胁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至溉瓶,卻和暖如春急鳄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背堰酿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工疾宏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人触创。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓坎藐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哼绑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岩馍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • bash 不支持浮點(diǎn)運(yùn)算,如果需要進(jìn)行浮點(diǎn)運(yùn)算抖韩,需要借助bc,awk 處理 expr命令 ======= 最開(kāi)始蛀恩,...
    杰倫哎呦哎呦閱讀 4,724評(píng)論 0 1
  • 0.1+0.2===0.3為false的說(shuō)明,下面是原因和解決方法茂浮。因?yàn)橛?jì)算機(jī)是二進(jìn)制的双谆,只能用0,1來(lái)表示數(shù)值席揽,...
    左左front閱讀 946評(píng)論 0 0
  • 今天遇到一個(gè)問(wèn)題: var a = 0.3 === 0.1 + 0.2 ; 打印a輸出居然為false顽馋, 在Jav...
    是leee啊閱讀 619評(píng)論 0 0
  • 運(yùn)算符是處理數(shù)據(jù)的基本方法,用來(lái)從現(xiàn)有的值得到新的值幌羞。JavaScript 提供了多種運(yùn)算符寸谜,本章逐一介紹這些運(yùn)算...
    徵羽kid閱讀 659評(píng)論 0 0
  • 定點(diǎn)小數(shù)運(yùn)算 來(lái)自:http://www.eepw.com.cn/article/17893.htm 在DSP世界...
    郝宇峰閱讀 9,007評(píng)論 0 2