1. 程式人生 > >圖形程式設計中常用浮點數及其精度剖析

圖形程式設計中常用浮點數及其精度剖析

    圖形學中經常要涉及使用各種格式的浮點數型別,如float,half,也會經常用到各種格式的浮點數型別PixelFormat,例如R10G10B10A2,R11G11B10,RGBHalf...。在合適的情況下使用合適的浮點數型別,是保證效率和效果的基礎。雖然有些是大學本科課程,但牢記於腦海並時刻保持對每種浮點數精度的敏感也不易,此文總結並詳細討論一下各種常見浮點數的精度,範圍,精度分佈及應用特點。

   1. 小數的二進位制表示

     首先浮點數只是一種小數的表示方式,整數的二進位制表示是一個簡單的十進位制到二進位制的轉換,即從右到左,每個二進位制的1表示1,2 ,4 , 8 , 16,例如111即7,而小數的二進位制則是從從左到右的每個1依次表示為1/2, 1/4, 1/8, 1/16,例如111.111即表示了1+2+4+0.5+0.25+0.125即7.875。

     我們知道小數和整數不一樣,小數在一段區間內是無限多個的,所以無論怎樣都不能連續的表示出一段區間內的所有小數,即小數的表示存在一個最低精度的,如果向上面那樣用三位表示小數部分,那小數的精度就只有1/8,即7.875可以表示,但是7.876就表示不出來了,也會變成7.875.

     上面這種表示方法小數點的位置是固定的,固定小數點前有多少bit表示整數,小數點後有多少bit表示小數,所以叫做定點數表示法,定點數的特點是表示的小數的精度是均衡的,例如fixed 11型別的定點數,在-2到2區間內的精度一直是1/256,因為他有8位表示小數。

      而浮點數是把小數拆成尾數(mantissa -M)和指數(exponent- E)來表示,根據IEEE754標準,表示一個小數的方法為V=(-1 )^{S} * M * 2^{E}

,拿float32為例子,s佔用1位,M佔用23位,E佔用8位,下面設M有m位,E有e位。

其中S是1bit的符號位,表正負

M是1.xxxxx的一個小數,而M正是對這個小數部分XXXX的二進位制表示

其中E是指數部分,E就相當於將二進位制的M中的小數點向右(E為正),或向左(E為負)移動e個位,這也是浮點數中浮點的意義,即浮點數是通過浮動1.xxx的小數點的位數來表示任意大小的小數,E的移動是2倍的改變,所以浮點數可以表示的範圍很大,但是是以損失精度為代價的。

浮點數中的E的正負同整數的正負表示不一樣,它不是補碼錶示法,即不用首位表正負,而是按照無符號處理的值減去整個值域的中位數成為最終的值,例如8位的E的中間數是127,那麼00000000就表示-127,而11111110則表示127,E的範圍是-2^{e-1} + 1

 到 2^{e-1}-1。(全1有特殊意義),float32 就是[-127,127]

此外浮點數的表示有一些特例,大部分浮點數型別要求可以表示一個無窮大值,所以浮點數在一些情況下不只是按照上面的公式計算數值,還有下面幾個特殊情況:

E全為0,此時M將變成0.xxxxx的形式,不然永遠不能表示0,所以M為全零,E為全零就是表示的浮點數0,如果E全零,而M不全零,那麼也按照上面公式將0.xxxxx向左移最大的127位,這個時候相當於表示了一個非常接近於0的一個小數(小數點後充填了127個0)。

E全為1,此時不按上面公式,如果M全為0,表示正負無窮大,如果M不全為0,表示不是一個合理的數字(NAN,not a number)。

    2.浮點數的精度和範圍

    浮點數的精度由整數部分的精度和小數部分的精度組成:

     關於整數部分的精度,浮點數可以保證在正負2^{m+1}範圍內的整數連續,這個+1是因為M的整數部分是1導致的(當然要保證E的最大表示範圍要大於等於M的位數),所以float32可以保證在正負2^24 之間的整數是同int32是可以對應的,但是超過 2^24的部分不能保證所有的整數部分都可以被表示,超過的部分只能表示2^24以內整數的2^N倍(因為這時沒有有效的尾數,只能繼續偏移小數點了,當然N還要取決於E的範圍),所以從float 強轉到int要保證準確的話,float的值最多就在2^24以內,即16777216以內,同樣double轉long時保證正確性,double的值最多也在2^53以內。

    相對於整數部分的精度,其實我們往往更關心小數部分的精度,因為通常浮點數的E的最大值要大於M的位數,所以在相當大的範圍內整數是能連續不丟失精度的,所以小數部分的精度就很關鍵。浮點數相比於定點數,在同樣bit數下面表示了更大的範圍,但是損失了精度,且有個特點,浮點數的精度隨著絕對值的增大而大幅度降低。

    M的bit數就定了這個小數最大的精度,因為M裡面存在了一個1,所以 浮點數的小數部分最多的有效數字是m+1 位,出現在-1到1之間,即0.xxx的時候xxx最多可能有m+1位,精度至少可以到達1/2^{m+1},float32 就是1/2^{24},當然如果e可以再往左浮動,還可以精確到更小,在E為全0時,可以表示到的最接近0 的那個正數是小數點之後有127+22=149個0,即2^-150,隨著浮點數的增大,其小數部分的有效數字隨著值增大而減少,1.xxxx的時候精度最多隻能到1/2^m了,2.xxxx就最多隻能到1/2^(m-1).

   所以總體上看,浮點數在-1到1之間,也就是0.xxxx的部分精度非常高,最少可以到1/2^(m+1),無限接近0的過程中對於float32最高可以到達1/2^(127+23)。隨著整數部分變大,小數精度就逐漸喪失,直到2^(m+1) -1 將完全沒有小數位。

  至於浮點數可以表示的極限值,首先它可以表示一個形式上的正負無窮大,除了無窮大之外,最大值出現在E為2^(e+1)-2  (對於float32即e為127,128是無窮大) 並且M為全1 的時候,這時候的值對於float32來說是(2^24 -1)* 2^(127-23),即2^128 - 2^104,最小值同樣。這個值大約是3.4e+38,其實討論這個極大值在多數情況意義並不是很大,因為它的精度已經降到了1.7e+38,所以對於浮點數如果拋開精度去談範圍已經沒有太大意義,使用浮點數一定要考察它在不同值區間上的精度變化。

3.圖形程式設計中常用的幾種浮點數及其精度隨值域的改變

圖形學中用於無壓縮的貼圖或者rt中,常用的浮點型別有很多,無論是R10G10B10A2,R11G11B10,RGBHalf,從組成的基礎上看,都是歸結到這幾種浮點格式:

32bit, 即上面舉例討論的最代表性的float型別,1位符號位,23位M,8位E,例如RGBA32F格式

16bit,即half,半精度的浮點型,1位符號位,10位M,5位E,例如RGBA16F格式

11bit,無符號浮點數,6位M,5位E,例如R11F_B11F_G10F格式

10bit,無符號浮點數,5位M,5位E,例如RGB10_A2格式

上面這些通常都可以表達HDR資訊,即可以大於1,而LDR格式型別如RGBA8則用8位表示小數,它就不是浮點數,可以理解是定點數,表示精度恆定間隔1/256的一些小數。

以10bit的浮點數為例子,繪製出它的精度誤差如下

其中藍色是8bit頂點數的誤差,黃色是10bit浮點數的,可以看到,隨著0.25,0.5,1的提升,它的誤差會變得非常大,

10bit的浮點數在1.xxxx之內的精度也只有1/32,大約0.03,要進入0.25以下才能精確到百分位,在7.xxx左右就降低到只有十分位了

11bit的浮點數在1.xxxx之內的精度有1/64,只有在0.5以內能保證百分位,在15.xxx左右降低到十分位。

而16bit的浮點數在15.xxxx之內都能保證百分位,在255.xxx能保持十分位,32bit的浮點數在2^21左右保證十分位,在2^17左右還能保證百分位。

由此可以看出類似10bit 11bit 這種所謂的小浮點數其實高精度區間非常有限,他們只適合表示差不多10一下的一些小數。

4.實際應用中的選擇

在圖形程式設計中,使用小數的地方很多,位置,uv,顏色,法線,pbr的引數等等,使用的bit數越高,精度效果越好,但是效能會越差。

對於位置來說,由於場景的範圍可能很大,且仍然需要精確到mm左右,所以一般都要用32bit的float,但有時對於超級大的場景來說即使用float32仍然不夠,當座標刻度大於2^21次即2097152左右就不能保證十分位了,比如說你場景單位是cm,這個值就大約是21km,超過21km的大場景地圖也並不是沒有的,這裡就捉襟見肘了。在UE4中,它為了保證這個毫米精度,噹噹前的攝像機視角超過這個2097152的座標位置,就會把場景的中心整體平移到攝像機這裡,這樣可以保證攝像機周圍的物體在渲染的時候,精度一定可以保證在mm的精度內。

對於uv來說,通常16bit的half已經足夠了,它在大約15以內具有百分位精度。

對於顏色,如果是LDR的值,大多數情況8bit的定點數是足夠的,如果是HDR,就又要取決於HDR的高值可能出現在多高,如果在8以內,RGB10A2是能夠保證十分位的,如果在16以內,可能需要升級到R11G11B10,如果有更高的值域範圍,那就需要升級到RGBA16F了。

對於法線,金屬度,光滑度等,通常因為可以歸一化01區間,所以8bit的頂點數就夠了。

總之我們的原則一定是在保證所需的效果的同時使用最低bit的浮點數格式,因為這意味著頻寬的節省,意味著alu的效率提升,最終就是幀率的提升和發熱量的減少。