本文分為以下幾個(gè)部分
- IEEE754標(biāo)準(zhǔn)
- Unity ShaderLab 中 float half fixed 的精度范圍
- 精度轉(zhuǎn)換
引言:
??對(duì)于精度敏感,對(duì)GPU編程是很有幫助的,在你的Shader中使用什么樣的精度類型,效率會(huì)高,這個(gè)是很重要的一點(diǎn)。之前寫過一個(gè)bug,超出half的精度了,Android手機(jī)上測(cè)試,高通的GPU效果是對(duì)的,但華為的麒麟GPU,就有問題了,所以精度溢出處理細(xì)節(jié)上應(yīng)該是有區(qū)別的。
1.IEEE754標(biāo)準(zhǔn)
面試官:float類型在c++中是怎么存儲(chǔ)的呢?
小明 : ……
wiki上的解釋很好 這里摘抄截取一下:
官方的術(shù)語(yǔ)可能還是有點(diǎn)官方,后面我會(huì)詳細(xì)解釋一下
- IEEE二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)(IEEE 754)是20世紀(jì)80年代以來最廣泛使用的浮點(diǎn)數(shù)運(yùn)算標(biāo)準(zhǔn),為許多CPU與浮點(diǎn)運(yùn)算器所采用。這個(gè)標(biāo)準(zhǔn)定義了表示浮點(diǎn)數(shù)的格式(包括負(fù)零-0)與反常值(denormal number),一些特殊數(shù)值((無窮(Inf)與非數(shù)值(NaN)),以及這些數(shù)值的“浮點(diǎn)數(shù)運(yùn)算符”;它也指明了四種數(shù)值舍入規(guī)則和五種例外狀況(包括例外發(fā)生的時(shí)機(jī)與處理方式)。
- IEEE 754規(guī)定了四種表示浮點(diǎn)數(shù)值的方式:?jiǎn)尉_度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實(shí)現(xiàn))。
類型 | 符號(hào)位(bit) | 指數(shù)偏移值(bit) | 分?jǐn)?shù)值(bit) |
---|---|---|---|
float(32bit) | 1 | 8 | 23 |
double(64bit) | 1 | 11 | 52 |
- 符號(hào)位:表示正負(fù)
- 指數(shù)偏移值:為了方便比較浮點(diǎn)數(shù)大小,移碼表示浮點(diǎn)數(shù)的指數(shù)部分,也叫階碼。以單精度浮點(diǎn)數(shù)(float)為例,它的指數(shù)域是8個(gè)比特,固定偏移值是2^(e-1) -1 也就是 2^7 - 1 = 127
- 分?jǐn)?shù)值:通俗理解,就是所謂的1.11111 * e^10 前面的1.11111。
舉個(gè)例子:
- 3.25 用二進(jìn)制表示
3 => 101
0.25 => 0.01
101.01 => 1.0101 * 2^2
那么在float中
符號(hào)位 是 0
指數(shù)偏移值是 2 + 127 => 10000001
分?jǐn)?shù)值: 1.0101 => 0101(去掉1) + 19個(gè)0 (達(dá)到23位)
**********劃重點(diǎn)*********
??敲黑板!!單精度float類型為例,指數(shù)表示的范圍是多少呢?
??來算一下,正常8 bit表示的十進(jìn)制數(shù)字范圍是 0~256,那么對(duì)于實(shí)際意義的指數(shù)來說就是 0 ~256 - 127 即 [-127 ,128],但是標(biāo)準(zhǔn)規(guī)定啦,指數(shù)為0和指數(shù)為2^8 -1 (即最大) 有其他作用,所以實(shí)際有效的指數(shù)范圍為 [-126, 127]。那么其實(shí)float的最大最小值就是 ±(2 - 2^-23) * 2^127。
那么前后兩個(gè)邊界值有啥特殊意義呢??
**********劃完了*********順著讀***************
兩種浮點(diǎn)數(shù)類型:
- 規(guī)約形式的浮點(diǎn)數(shù):浮點(diǎn)數(shù)中的指數(shù)部分編碼值在 [0, 2^e - 1] 之間,并且科學(xué)計(jì)數(shù)法分?jǐn)?shù)部分最高有效位是1。
- 非規(guī)約形式的浮點(diǎn)數(shù):浮點(diǎn)數(shù)的指數(shù)部分的編碼是0,分?jǐn)?shù)部分非零。一般只有數(shù)字相當(dāng)接近0的時(shí)候才會(huì)這么表示(為了解決填補(bǔ)絕對(duì)值意義下最小規(guī)格數(shù)與0的距離,大概解釋一下 比如 (2^-20 * 2^-120) - (2^-21 * 2^-120), 這個(gè)精度已經(jīng)超出了float類型規(guī)約數(shù)的2^-126,其精度表示不了了 下溢為0了。所以最高位設(shè)置為0之后,又會(huì)有23為分?jǐn)?shù)值的精度去表示小數(shù))
這個(gè)地方蠻繞的,大概理解一下就好,最高位設(shè)置為0是針對(duì)突然式下溢出的解決辦法,即漸進(jìn)式下溢出。使用分?jǐn)?shù)值(也就是尾數(shù))的精度去補(bǔ)充表示指數(shù)位表示不了的精度!差不多差不多就是這個(gè)意思。
2.float half fix (unity文檔上所述)
- float 32位單精度浮點(diǎn)數(shù),遵守IEEE754標(biāo)準(zhǔn)
范圍 : ±(2?2^?23) × 2^127 ≈ ±3.4×10^38
精度 : 2^-126 * 2^-23 - half 16位中精度浮點(diǎn)數(shù),結(jié)構(gòu)未知
范圍 : [-6萬, +6萬],
精度 :十進(jìn)制小數(shù)點(diǎn)后3.3位 - fixed 11位低精度浮點(diǎn)數(shù),結(jié)構(gòu)未知
范圍 :[-2,2]
精度 :1/256
3.精度轉(zhuǎn)換
??一直在想,對(duì)于GPU來講精度轉(zhuǎn)換是不是一件昂貴的事情。對(duì)于Unity ShaderLab來說,片元函數(shù)中采樣的tex2D函數(shù)返回的是float4,片元函數(shù)返回的精度是fixed4,所以無論如何一定會(huì)有精度轉(zhuǎn)換的操作,所以我個(gè)人認(rèn)為降低精度越早越好,以提高數(shù)學(xué)運(yùn)算的效率,fixed的小數(shù)部分足夠進(jìn)行點(diǎn)乘或者其他數(shù)學(xué)運(yùn)算。
最后 用Unity官方對(duì)于如何使用精度的推薦為結(jié)尾:
Use lowest precision that is possible; this is especially important on mobile platforms like iOS and Android. Good rules of thumb are:
1.For colors and unit length vectors, use fixed.
2.For others, use half if range and precision is fine; otherwise use float.