本文嘗試著將以下內(nèi)容做一個淺顯的解釋,主要包括浮點數(shù)為什么是不精確的,浮點數(shù)為什么不能用==和祷嘶!=直接比較俗他,以及浮點數(shù)的比較方法等幾個方面脖捻。如果那個地方說的不對還請各位看官不吝賜教!歡迎大家評論區(qū)討論兆衅。
IEEE 754 --- 二進制浮點數(shù)算術(shù)標(biāo)準(zhǔn)
浮點格式是一種數(shù)據(jù)結(jié)構(gòu)地沮,用于指定包含浮點數(shù)的字段嗜浮,這些字段的布局及其算術(shù)解釋。自計算機發(fā)明以來摩疑,出現(xiàn)了許多種不同的浮點數(shù)表達方式危融,目前最通用的是IEEE二進制浮點數(shù)算是標(biāo)準(zhǔn)-IEEE 754.
IEEE 754規(guī)定了四種表示浮點數(shù)值的方式:單精確度(32位)、雙精確度(64位)雷袋、延伸單精確度(43比特以上吉殃,很少使用)與延伸雙精確度(79比特以上,通常以80位實現(xiàn))楷怒。只有32位模式有強制要求蛋勺,其他都是選擇性的。大部分編程語言都有提供IEEE浮點數(shù)格式與算術(shù)率寡,但有些將其列為非必需的迫卢。例如,IEEE 754問世之前就有的C語言冶共,現(xiàn)在有包括IEEE算術(shù)乾蛤,但不算作強制要求(C語言的float通常是指IEEE單精確度,而double是指雙精確度)捅僵。
二進制如何表示浮點數(shù)的
所有的數(shù)據(jù)在計算機內(nèi)都已二進制形式表示家卖,那么浮點數(shù)的在計算機內(nèi)是如何以二進制形式存儲的呢?
二進制浮點數(shù)是以符號數(shù)值表示法的格式存儲——浮點數(shù)由三部分組成:符號(sign部分庙楚,表示符號0正上荡,1負數(shù)),指數(shù)(exponent部分馒闷,表示指數(shù)位)酪捡,和尾數(shù)(fraction部分,表示有效數(shù)字纳账,大于等于1逛薇,小于2)。
單精度浮點數(shù)來說疏虫,sign占1位永罚,exponent部分占8位,fraction部分占23位卧秘。
雙精度浮點數(shù)來說呢袱,sign占1位,exponent部分占11位翅敌,fraction部分占52位羞福。
那么該如何理解上面這段話呢?舉例來說蚯涮,十進制的5.0坯临,寫成二進制是101.0焊唬,相當(dāng)于1.01×2^2。那么看靠,按照上面那段話赶促,可以得出符號位sign為0,fraction部分為1.01挟炬,exponent部分為2鸥滨。
二進制十進制相互轉(zhuǎn)換
先介紹一下轉(zhuǎn)換采用的規(guī)則:
- 二進制轉(zhuǎn)十進制規(guī)則:
R進制轉(zhuǎn)換成十進制:基數(shù)為R的數(shù)字,只要將各個數(shù)字與它的權(quán)相乘谤祖,其積相加婿滓,和數(shù)就是十進制數(shù)。
- 十進制轉(zhuǎn)二進制規(guī)則:
整數(shù)部分:正整數(shù)轉(zhuǎn)成二進制粥喜。要點一定一定要記住哈:除二取余凸主,然后倒序排列,高位補零额湘。
小數(shù)部分:小數(shù)部分的轉(zhuǎn)換規(guī)則:十進制小數(shù)轉(zhuǎn)換成二進制小數(shù)采用"乘2取整卿吐,順序排列"法。具體做法是:用2乘十進制小數(shù)锋华,可以得到積嗡官,將積的整數(shù)部分取出,再用2乘余下的小數(shù)部分毯焕,又得到一個積衍腥,再將積的整數(shù)部分取出,如此進行纳猫,直到積中的小數(shù)部分為零婆咸,如果得不到零就截取達到所要求的精度就可以。 然后把取出的整數(shù)部分按順序排列起來芜辕,先取的整數(shù)作為二進制小數(shù)的高位有效位尚骄,后取的整數(shù)作為低位有效位。
例1: 把二進制數(shù)110.0101轉(zhuǎn)換為十進制數(shù):
110.0101=12^2+121+0*20+02^(-1)+12(-2)+0*2(-3)+1*2^(-4)=6.3125
例2: 把十進制數(shù)8.125轉(zhuǎn)換成二進制數(shù):
整數(shù)部分:
13 = 1000物遇,
小數(shù)部分:
0.125x2 = 0.25, 整數(shù)位是0->1000.0;
0.25x2 = 0.5, 整數(shù)位是0->1000.00;
0.5x2 = 1, 整數(shù)位是1->1000.001;
所以8.125 轉(zhuǎn)換成二進制是1000.001
例3: 把十進制數(shù)0.1轉(zhuǎn)換為二進制數(shù)。
0.1x2 = 0.2, 整數(shù)位是0 -> 0.0;
0.2x2 = 0.4, 整數(shù)位是0 -> 0.00;
0.4x2 = 0.8, 整數(shù)位是0 -> 0.000;
0.8x2 = 1.6, 整數(shù)位是1 -> 0.0001;
0.6x2 = 1.2, 整數(shù)位是1 -> 0.00011;
0.2x2 = 0.4, 整數(shù)位是0 -> 0.000110;
...
得到一個無限循環(huán)的二進制小數(shù)憾儒,顯然用有限的字長是無法表示0.1的询兴。告訴你一個悲傷的消息0.2,0.4起趾,0.6诗舰,0.8,0.3训裆,0.7眶根,0.9都是無法精確表示的蜀铲。只有0.5可以用二進制精確表示。拿這些無法精確表示的數(shù)該怎么辦呢属百?我們在十進制轉(zhuǎn)二進制的規(guī)則里說過记劝,如果沒有得到0,就截取達到所要求的精度就可以族扰。由此可知厌丑,一個十進制小數(shù)要能用二進制浮點數(shù)精確表示,最后一位必須是5.當(dāng)然這是必要條件渔呵,并非充分條件怒竿。由此可以看出,一個十進制數(shù)能否用二進制浮點數(shù)精確便是扩氢,關(guān)鍵在于小數(shù)部分耕驰。
float比較方法
不可將浮點變量用“==”或“!=”做直接比較录豺,而應(yīng)該設(shè)法轉(zhuǎn)化成能用“>=”或“<=”作比較的形式朦肘。由上可知計算機在處理浮點數(shù)的時候是有誤差的,所以判斷兩個浮點數(shù)是不是相同巩检,是要判斷是不是落在同一個區(qū)間的厚骗,這個區(qū)間就是 [-EPSINON,EPSINON] EPSINON一般很小,10的-6次方以下兢哭,這個值肯定是越小越精確领舰,不過也看具體的個情況,夠用就好迟螺。
- float與“零值”比較
一般情況我們會定義一個很小的接近于0的值比如0.00001或者更小的冲秽,然后浮點數(shù)跟這個數(shù)作比較。
假設(shè)x是一個浮點數(shù):
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
或者
if( abs(a-b) < FLT_EPSILON)
- float與float比較
假設(shè)x矩父,y是兩個浮點數(shù)是否相等:
const float EPSINON = 0.00001;
if( abs(a-b) <= EPSINON )
還有一種方法就是擴大再取整锉桑,比如4.113、4.114窍株,直接比較有可能為 false民轴,但是都擴大一千倍,然后強制轉(zhuǎn)換為 int 類型球订,再用 == 比較就可以了后裸。