故事開始于Unity Doc中關于Half的進度的解釋
Medium precision: half
half
Medium precision floating point value; generally 16 bits (range of –60000 to +60000, with about 3 decimal digits of precision).Half precision is useful for short vectors, directions, object space positions, high dynamic range colors.
不知是人入中年,還是隔得太久,早已把一些基礎的東西忘卻了。看到這裡關于精确的描述,感覺有點不可思議,16bit,最多也就2的16次方=65535中可能性,怎麼能表示這麼大的範圍呢?後面經過Google的腦補,才明白過來,因為float的有損,才使得這個成為可能,而float精辟的地方也就在表示的每個數字的時候,誤差相對于它的絕對值都是很小的,即大數誤差較大,小數誤差較小。這裡備忘一下。
IEEE 754 标準指定了一個 binary16 要有如下的格式:
- Sign bit(符号位): 1 bit
- Exponent width(指數位寬): 5 bits
- Significand precision(尾數精度): 11 bits (有10位被顯式存儲)
按如下順序排列:

除非指數位全是0,否則就會假定隐藏的起始位是1。是以隻有10位尾數在記憶體中被顯示出來,而總精度是11位。據IEEE 754的說法,雖然尾數隻有11位,但是尾數精度是11位的(log10(211) ≈ 3.311 十進制數).
而對于指數位,25=32,考慮到正負,範圍為-15~16,由于浮點型到二進制的轉換方法為1.xxx*2n,是以最大值為216=65535,加上符号位,是以才有取值範圍為-6000~6000的說法,因為1.xxx*2n,因為小數點前面一直是1,是以小數點後能有3位精度。
附:float的二進制表示
C語言和C#語言中,對于浮點類型的資料采用單精度類型(float)和雙精度類型(double)來存儲,float資料占用32bit,double資料占用64bit,我們在聲明一個變量float f= 2.25f的時候,是如何配置設定記憶體的呢?如果胡亂配置設定,那世界豈不是亂套了麼,其實不論是float還是double在存儲方式上都是遵從IEEE的規範的,float遵從的是IEEE R32.24 ,而double 遵從的是R64.53。
無論是單精度還是雙精度在存儲中都分為三個部分:
- 符号位(Sign) : 0代表正,1代表為負
- 指數位(Exponent):用于存儲科學計數法中的指數資料,并且采用移位存儲
- 尾數部分(Mantissa):尾數部分
其中float的存儲方式如下圖所示:
而雙精度的存儲方式為:
R32.24和R64.53的存儲方式都是用科學計數法來存儲資料的,比如8.25用十進制的科學計數法表示就為:8.25*
,而120.5可以表示為:1.205*
,這些國小的知識就不用多說了吧。而我們傻蛋計算機根本不認識十進制的資料,他隻認識0,1,是以在計算機存儲中,首先要将上面的數更改為二進制的科學計數法表示,8.25用二進制表示可表示為1000.01,我靠,不會連這都不會轉換吧?那我估計要沒轍了。120.5用二進制表示為:1110110.1用二進制的科學計數法表示1000.01可以表示為1.0001*
,1110110.1可以表示為1.1101101*
,任何一個數都的科學計數法表示都為1.xxx*
,尾數部分就可以表示為xxxx,第一位都是1嘛,幹嘛還要表示呀?可以将小數點前面的1省略,是以23bit的尾數部分,可以表示的精度卻變成了24bit,道理就是在這裡,那24bit能精确到小數點後幾位呢,我們知道9的二進制表示為1001,是以4bit能精确十進制中的1位小數點,24bit就能使float能精确到小數點後6位,而對于指數部分,因為指數可正可負,8位的指數位能表示的指數範圍就應該為:-127-128了,是以指數部分的存儲采用移位存儲,存儲的資料為中繼資料+127,下面就看看8.25和120.5在記憶體中真正的存儲方式。
首先看下8.25,用二進制的科學計數法表示為:1.0001*
按照上面的存儲方式,符号位為:0,表示為正,指數位為:3+127=130 ,位數部分為,故8.25的存儲方式如下圖所示:
而單精度浮點數120.5的存儲方式如下圖所示:
那麼如果給出記憶體中一段資料,并且告訴你是單精度存儲的話,你如何知道該資料的十進制數值呢?其實就是對上面的反推過程,比如給出如下記憶體資料:0100001011101101000000000000,首先我們現将該資料分段,0 10000 0101 110 1101 0000 0000 0000 0000,在記憶體中的存儲就為下圖所示:
根據我們的計算方式,可以計算出,這樣一組資料表示為:1.1101101*
=120.5
而雙精度浮點數的存儲和單精度的存儲大同小異,不同的是指數部分和尾數部分的位數。是以這裡不再詳細的介紹雙精度的存儲方式了,隻将120.5的最後存儲方式圖給出,大家可以仔細想想為何是這樣子的
參考:
https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
https://zh.wikipedia.org/zh-cn/%E5%8D%8A%E7%B2%BE%E5%BA%A6%E6%B5%AE%E7%82%B9%E6%95%B0