float 在 CUDA

這篇文章主要爲了說明float的相關背景和float在CUDA中的特性。
先說說 float 相關背景:
關於 float 在 MSDN 中的說明可以參考:

https://msdn.microsoft.com/zh-cn/library/hd7199ke.aspx

浮點數使用 IEEE 75(電氣和電子工程師協會)格式。 浮點類型的單精度值具有 4 個字節,包括一個符號位、一個 8 位 excess-127 二進制指數和一個 23 位尾數。 尾數表示一個介於 1.0 和 2.0 之間的數。 由於尾數的高順序位始終爲 1,因此它不是以數字形式存儲的。 此表示形式爲 float 類型提供了一個大約在 3.4E–38 和 3.4E+38 之間的範圍。

這裏的有效位就是數據表示的精度,6~7位有效數字。

float總結:
共計 32 位,4 字節
由最高到最低位分別是第 31 、 30 、 29 、……、 0 位
31 位是符號位, 1 表示該數爲負, 0 反之。
30-23 位,一共 8 位是指數位。
22-0 位,一共 23 位是尾數位。

更詳細表示參見博文:浮點數在計算機中存儲方式(轉) 

注意點:

1. MSDN: “由於指數是以無符號形式存儲的,因此指數的偏差爲其可能值的一半。 對於 float 類型,偏差(offset 偏移)爲 127;對於 double 類型,偏差(offset 偏移)爲 1023。 您可以通過將指數值減去偏差值來計算實際指數值。 ”

這句話中提到指數是以無符號形式存儲的,但是我們知道指數是有正有負的,這裏該如何理解?

這篇博文中提到:

再回來看指數,一共 8 位,可以表示範圍是 0 - 255 的無符號整數,也可以表示 -128 - 127 的有符號整數。但因爲指數是可以爲負的,所以爲了統一把十進制的整數化爲二進制時,都先加上 127 ,在這裏,我們的 16 加上 127 後就變成了 143 ,二進制表示爲: 10001111

2. MSDN:“存儲爲二進制分數的尾數大於或等於 1 且小於 2。 對於 float 和 double 類型,最高有效位位置的尾數中有一個隱含的前導 1,這樣,尾數實際上分別爲 24 和 53 位長,即使最高有效位從未存儲在內存中也是如此。 ”

這句話中提到最高有效位位置的尾數中有一個隱含的前導1,“120.5用二進制表示爲:1110110.1,1110110.1可以科學記數法表示爲1.1101101*2^6,任何一個十進制數的二進制科學計數法表示都爲1.xxx*2^n(xxx是0,1表示的數字), 尾數部分就可以表示爲xxxx,第一位都是1嘛,幹嘛還要表示呀?可以將小數點前面的1省略,所以23bit的尾數部分,可以表示的精度卻變成了 24bit,道理就是在這裏。

那24bit能精確到小數點後幾位呢,我們知道9的二進制表示爲1001,所以4bit能精確十進制中的1位小數點, 24bit就能使float能精確到小數點後6位,而對於指數部分,因爲指數可正可負,8位的指數位能表示的指數範圍就應該爲:-127-128了,所以指數部分的存儲採用移位存儲,存儲的數據爲原數據+127”。

指數有正有負,存儲上卻是用無符號來存儲,那就再存入之前入一些偏移:在這篇博文中也提到:“再回來看指數,一共 8 位,可以表示範圍是 0 - 255 的無符號整數,也可以表示 -128 - 127 的有符號整數。但因爲指數是可以爲負的,所以爲了統一把十進制的整數化爲二進制時,都先加上 127 ,在這裏,我們的 16 加上 127 後就變成了 143 ,二進制表示爲: 10001111”。

部分摘自:浮點數在計算機中存儲方式(轉)  

3. 浮點數對無窮大和非數也進行了表示:

Also, encodings to represent infinity and not-a-number (NaN) data are reserved. The IEEE 754 Standard [2] describes floating point encodings in full.

4. 浮點數精度問題:並非所有的實數都能被浮點數精確的表示,就是十進制小數在向二進制小數轉換的過程中,會發現尾數的最大長度 23 位也除不盡,這就產生了浮點數精度問題。

例如分數2/3的二進制表示爲 0.10101010... 是一個無窮的二進制小數,那麼2/3就必須先進行近似以便用有限精度的浮點數來表示。而近似的規則和模式在 IEEE 754 中也做了詳細的說明。這裏不做詳細展開,最常用的就是 round-to-nearest 方式。

2/3在這種方式下表示爲:

指數-1被存爲正整數 (-1) + offset,其中對於 float 類型,offset 爲 127;對於 double 類型,offset 爲 1023。


再說說 float 在 CUDA 中的特性。

在 CUDA 中浮點數的詳細說明可以參見官方文檔:Floating Point and IEEE 754

該文檔將 CUDA 中的浮點數涉及到的問題描述得及其細緻,就其中幾點總結如下:

1. IEEE 754 標準要求對一系列操作(加減乘除開方等)的支持,這些操作對給定的格式和近似模式都保證在不同的操作實現方式下都能產生一樣的結果,即與硬件平臺無關。

2. 數學上數字運算規則和性質對浮點數來說並非是一定成立的,這是由於浮點數精度限製造成的。

例如數學上(A + B) + C = A + (B + C)是成立的,如果用 rn(x) 表示依據 IEEE 754 標準對 x 的近似操作,那麼 rn(rn(A + B) + C) 和 rn(A + rn(B + C)) 與數學上的結果都不相同。

這表明相同的計算會產生不同的結果,即使他們所有的基本操作都是依據 IEEE 754 標準執行的。而結果的不同並不取決於執行主體,任何一個支持單精浮點數的微處理器 CPU,GPU 都會生成相同的結果。

3. FMA

在2008年 IEEE 754 標準引入FMA,而FMA 計算 rn(X * Y +Z) 只需要一步近似處理,因此較之前的 rn(rn(X * Y) + Z)更加精確,官方文檔中示例很好的說明了這一點。而選擇是否使用FMA是取決於你的平臺和編譯器的。

通過查看自己平臺的處理器支持的指令集: AVX,AVX2,FMA 來知道是否支持 FMA

4. 精度示例:向量點乘

恰好最近在進行 CUDA 點乘運算,這個示例很好的解釋了我遇到的現象。

在數學上,這個運算很簡單也沒有歧義,但是如果我們採用不同的策略,採用加,乘,FMA間不同的組織方式的話,就會產生不同的結果。以上的討論都是在 IEEE 754 下。

方法1:serial,通過一個循環,串行計算,將乘和加分開

方法2:FMA 方法,將方法1中的加乘放在一起

顯然方法2要比方法1精確。

方法3:parallel,分治的思想

這是一種遞歸的策略,由於各個部分是相互獨立的,因此也可以成爲並行方法,雖然實現上也可以採用串行。

三種方法在都使用 IEEE 754 標準下的比較:

• a = [1.907607, -.7862027, 1.148311, .9604002]
• b = [-.9355000, -.6915108, 1.724470, -.7097529]

Read more at: http://docs.nvidia.com/cuda/floating-point/index.html#ixzz3YQmGKgkh
Follow us: @GPUComputing on Twitter | NVIDIA on Facebook

可以看到每種方法產生的結果都與正確結果不太一樣。一般來說精確度上有:FMA > parallel > serial。

5.  CUDA 對浮點數的支持

Current generations of the NVIDIA architecture such as Tesla Kxx, GTX 8xx, and GTX 9xx, support both single and double precision with IEEE 754 precision and include hardware support for fused multiply-add in both single and double precision.
CUDA 中 GPU 對特性的支持反映在 capability 號上。2.0及其以上均支持以上特性。

IEEE 754 支持四種近似模式:round-to-nearest(default), round towards positive, round towards negative, and round towards zero
編譯器保留字可以指定近似模式:__fadd_[rn | rz | ru | rd] (x, y) etc. 具體參見手冊。
編譯也可以選擇也可以相關參數,具體參見手冊。

6. 一些建議

1.)使用 FMA

2.)仔細檢查結構

3.)瞭解自己 GPU 的capability

4.)儘量使用 CUDA 數學庫函數

至此我們對 CUDA 中的浮點數略知一二了。

再次說明,以上均來自官方文檔:Floating Point and IEEE 754


在CUDA中,對雙精度數支持還有一定要求:

設置GPU架構 sm_13 (如果你的GPU支持),命令行: -arch=sm_13

細節可以參考: Double precision floating point in CUDA




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章