對(duì)Shader中浮點(diǎn)數(shù)的思考


本文分為以下幾個(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))。

小端序表示
一個(gè)浮點(diǎn)數(shù) (Value) 的表示其實(shí)可以這樣表示:
也就是浮點(diǎn)數(shù)的實(shí)際值,等于符號(hào)位乘以指數(shù)偏移值(exponent bias)再乘以分?jǐn)?shù)值(fraction)。

類型 符號(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è)意思。
特殊的規(guī)約 (劃黑板拋出的兩個(gè)問題,指數(shù)為0和指數(shù)為2^e - 1)

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.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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