首先總結一下計算機中的浮點數的存儲。linux
浮點數的標準是IEEE-754,規定了浮點數的存儲都是經過科學計算法來存儲的,n2-e的表示。算法
浮點數首先分爲,定浮點(fixed-point)和浮點(float-point),定浮點就是說e的值是不變的。編程
目前浮點的計算都是將浮點轉換爲定浮點來計算,由此衍生出,單精度浮點和雙精度浮點。app
浮點數的存儲,前半部分表示exponent(能夠是負數,使用補碼錶示),後半部分表示fraction(規定fraction必須是1.x的格式)。ide
單精度的儲存位寬是32bit,e佔8bit,最大表示-127----128,fraction佔23bit。函數
雙精度的存儲位寬是64bit,e佔11bit,最大表示-1023---1024,fraction佔52bit。性能
e的表示採用以偏置形式表示的有符號整數,單精度的e,計算應該表示爲e+127從而消除負數。雙精度的e,計算應該表示爲e+1023。優化
fraction採用小數的表示方法,小數點以前除2得餘數爲二進制表示。小數點以後乘2得整數部分未二進制表示:ui
176.0625表示爲單精度浮點數,scala
176/2 得 88,餘0
88/2 得 44,餘0
44/2 得 22,餘0
22/2 得 11,餘0
11/2 得 5, 餘1
5/2 得2, 餘1
2/2 得1, 餘0
1/2 得0, 餘1 商爲零結束。
小數點以前的表示爲1011_0000
0.0625*2 得0.125, 整數部分爲0
0.125*2 得0.25, 整數部分爲0
0.25*2 得0.5, 整數部分爲0
0.5*2 得1, 整數部分爲1,小數部分爲0,結束。
小數點以後的表示爲0001。(小數部分不必定能夠被準備的表示出來,小數以5結尾爲必要條件)
176.0625表示爲單精度浮點數,1011_0000.0001
好比1.01 X 2-3,其中exponent表示-3+127=124(0111_1100),.01表示fraction。
單精度和雙精度的,精度對比:
浮點數的規則化(normalized):
fraction必須是|1.x|的格式;
非規則化的數:
正零:全部bit都爲0;
負零:除了符號位,都爲0;
無窮大:exponent的全部bit都爲1;fraction的全部值都爲0;
負無窮大:exponent的全部bit都爲1;fraction的全部值都爲0,符號位爲1;
非法數值:exponent的全部bit都爲1;fraction的值不全爲0;NaN(Not a Number)
浮點數的計算:
1)判斷是否有操做數爲0;
2)對階:小階向大階對齊,階小的那個數(看正負),exponent加n,fraction右移n位。
3)加減運算,fraction作加減運算,exponent不變。
4)結果規格化。(這時會有舍入處理,IEEE754規定了幾種舍入)
判斷溢出,浮點只有exponent的上溢,(正數相加不爲負,負數相加不爲正);
加減:
乘法:
除法:
平方根:
Basic op:
arm的vfp實現有vfpv3和vfpv4兩種,vfpv4相比較與vfpv3主要增長了half-precision extension和乘加的指令。
arm的vfp能夠實現爲32個或16個double-word register,分別以vfpv3-D32和vfpv3-D16來表示。 可是neon和vfp同時實現時,vfp只能夠實現爲vfp-D32。
vfp的主要控制寄存器:
1) FPSCR(Status Control reg),保存FP運算以後的flags,rounding options,enable exception trapping。
VCMP d0,d1
VMRS APSR_nzcv, FPSCR,將SCR中的flags加載到apsr中,才能進行比較指令的跳轉
BNE label
2) FPEXC(Exception reg),處理各類exception,包括FP計算過程當中的overflow,underflow,inexact(須要進行rounding),invalid(NaN),Division by zero等,
硬件FP單元的使用,須要在編譯器(gcc)中進行選項標註:
1) -mfpu=vfp/neon/vfpv3/vfpv4/vfpv4-d16等。指明硬件FP單元;
2) -mfloat-abi = softfp、hardfp,指明abi接口,進行正常的context switching過程當中的寄存器進棧出棧。
若是是arm compiler,須要的選項,--fpu=vfpv3/vfpv3_d14等,--apcs=/hardfp或者/softfp等。
arm的SIMD指令的發展:
SIMD,通常應用在數據量較大的場合,使用一條指令,加載多個一樣type和size的數據,並對數據進行並行處理;
例如,2個32bit的數據加法,被替換爲,4個8bit的數據加法。
armv6,提出一些SIMD的指令,將多個16bit,8bit的數據加載到32bit寄存器中,可是並無單獨的執行單元,
也沒有單獨的流水線。指令名字就是在以後加16或8的後綴。使用32bitSIMD。
armv7,引入了advanced SIMD,定義了本身的向量寄存器,32*64bit register file,本身的流水線執行單元。這些SIMD的擴展被稱爲NEON。
向量寄存器,是一組64bit的雙字,或128bit的四字,使用64bit或者128bit的SIMD。
NEON指令能夠實現:
1) 存儲空間訪問;
2) 在NEON和general寄存器之間的數據copy;
3) 數據類型轉換;
4) 數據計算;支持8bit(vido image的pixel data),16bit(audio codecs data),32bit,64bit的符號數整型,32bit/16bit的單精度浮點,
neon能夠和vfp同時使用,但因爲寄存器是公用的,vfp必須實現爲D32 form,
V7中NEON與VFP的計算對比:
1) NEON是SIMD指令,主要處理vector數據,並行度也高(最可能是4),vfp是scalar指令,SISD的形式處理FP。
2) VFP支持全IEEE754的標準,NEON只支持單精度浮點,不支持square root或者divide。
NEON指令:
VADD.I16 q0, q1, q2,表示使用8個16bit的並行加,
VMULL.S16, Q0, D2, D3,表示使用4個16bit的並行乘,
NEON在使用gcc編譯器時的選項:
1) 編譯彙編(指明abi接口和fpu接口),arm-none-linux-gnueabi-as -mfpu=neon asm.s
2) 關聯函數intrinsics, #include <arm_neon.h>
uint32x4_t double_elements(uint32x4_t input)
{ return(vaddq_u32(input, input));
}
arm-none-linux-gnueabi-gcc -mfpu=neon intrinsic.c
3) 優先矢量化(儘量的使用SIMD來提升性能),arm-none-linux-gnueabi-gcc -mfpu=neon -ftree-vectorize -c vectorized.c
4) 使用優化庫,OpenMAX,須要下載安裝,程序中加入頭文件,#include <omxSP.h>
VFP在使用時,與NEON的編程相似,寄存器一部分是共享的。
在armv8中,有分別針對aarch32(等同於v7)和aarch64的NEON指令;v8中的aarch64中使用32*128bit的register file;
v8中FP和NEON均做爲一個標準部件,繼承在core內部。
aarch64中的neon徹底支持IEEE754標準的全部FP操做,雙精度,NaN handling,rounding等。
在v8中,neon指令和fp指令與a64的指令相同,根據以後的操做數寄存器來區分(v7中neon,fp指令前加v):