這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go提供了兩種size的浮點數,float32和float64。它們的算術規範是由IEEE754國際標準定義,現代CPU都實現了這個規範。
浮點數能夠表示的範圍可以從很小到很巨大,這個極限值範圍可以在math包中擷取,math.MaxFloat32表示float32的最大值,大約是3.4e38,math.MaxFloat64大約是1.8e308,兩個類型最小的非負值大約是1.4e-45和4.9e-324。
float32大約可以提供小數點後6位的精度,作為對比,float64可以提供小數點後15位的精度。通常情況應該優先選擇float64,因此float32的精確度較低,在累積計算時誤差擴散很快,而且float32能精確表達的最小正整數並不大,因為浮點數和整數的底層解釋方式完全不同,具體見IEEE754詳解。
var f float32 = 16777216 // 1 << 24fmt.Println(f == f+1) // "true"!
浮點數字面量可以使用十進位數字表示:
const e = 2.71828 // (非精確值)
小數點前面或者後面的數字都可以省略,例如:.707 , 1. ,對於那種很小或者很大的數值最好用科學計數法,在指數前加上e或者E:
const Avogadro = 6.02214129e23 // 阿伏伽德羅常數const Planck = 6.62606957e-34 // 普朗克常數
fmt列印浮點數時,若使用%g參數,會採用更高的精度更緊湊的表現形式進行列印,但是在列印表格式資料時,%e(指數)或者%f(非指數的)的形式可能更合適,上面三個參數都可以控制列印的寬度和精度:
for x := 0; x < 8; x++ { fmt.Printf("x = %d e^x = %8.3f\n", x, math.Exp(float64(x)))}上面的代碼使用了小數點後3位的精度進行列印,列印寬度是8個字元:
x = 0 ex = 1.000x = 1 ex = 2.718x = 2 ex = 7.389x = 3 ex = 20.086x = 4 ex = 54.598x = 5 ex = 148.413x = 6 ex = 403.429x = 7 ex = 1096.633
math包不僅包含了大量的數學函數,還包含了IEEE754規範下特殊浮點數的建立和查看:正無窮,表明數字太大溢出的情況;負無窮,表示被0除的結果;NaN(不是一個數值),用來表示無效運算的結果,例如 0 / 0, math.Sqrt(-1)。
var z float64fmt.Println(z, -z, 1/z, -1/z, z/z) // "0 -0 +Inf -Inf NaN"
函數math.IsNaN測試一個數值是否是NaN,math.NaN會返回一個NaN值。雖然可以在數值計算中用NaN做為一個哨兵值,但是測試一個計算的結果是否等於NaN是很危險的,因為任何值跟NaN比較的結果都是false:
nan := math.NaN()fmt.Println(nan == nan, nan < nan, nan > nan) // "false false false"
如果一個返回浮點數的函數可能失敗,那最好還是單獨的報告失敗:
func compute() (value float64, ok bool) { // ... if failed { return 0, false } return result, true}下面的程式示範了通過浮點數計算來產生圖形,使用了z = f(x,y)來進行三維建模,使用了SVG格式做映像輸出,SVG是一個用於繪製向量線的XML標準。展示了sin(r)/r函數產生的圖形,r = sqrt(x*x + y*y):
// Surface computes an SVG rendering of a 3-D surface function.package mainimport ( "fmt" "math")const ( width, height = 600, 320 // canvas size in pixels cells = 100 // number of grid cells xyrange = 30.0 // axis ranges (-xyrange..+xyrange) xyscale = width / 2 / xyrange // pixels per x or y unit zscale = height * 0.4 // pixels per z unit angle = math.Pi / 6 // angle of x, y axes (=30°))var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)func main() { fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+ "style='stroke: grey; fill: white; stroke-width: 0.7' "+ "width='%d' height='%d'>", width, height) for i := 0; i < cells; i++ { for j := 0; j < cells; j++ { ax, ay := corner(i+1, j) bx, by := corner(i, j) cx, cy := corner(i, j+1) dx, dy := corner(i+1, j+1) fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n", ax, ay, bx, by, cx, cy, dx, dy) } } fmt.Println("</svg>")}func corner(i, j int) (float64, float64) { // Find point (x,y) at corner of cell (i,j). x := xyrange * (float64(i)/cells - 0.5) y := xyrange * (float64(j)/cells - 0.5) // Compute surface height z. z := f(x, y) // Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy). sx := width/2 + (x-y)*cos30*xyscale sy := height/2 + (x+y)*sin30*xyscale - z*zscale return sx, sy}func f(x, y float64) float64 { r := math.Hypot(x, y) // distance from (0,0) return math.Sin(r) / r}corner函數返回兩個值,分別是網格頂點的x,y座標。
如果要深入解釋映像產生的原理,我們還需要一些幾何學知識。但是這裡會跳過這些幾何學原理,畢竟這個程式主要是為了示範浮點數的運算。程式本質上是三個座標系間的映射,如所示,第一個是100*100的二維網格,每個儲存格都有座標(i,j),從座標系原點(0,0)開始延伸。繪製時是從遠處開始繪製,因此遠處先繪製的多邊形可能被後繪製的多邊形覆蓋。
第二個座標系是三維網格組成的,座標(x,y,z),其中x和y是i和j的線性函數,通過座標轉換把原點變為中心點,然後通過xyrange進行縮放。高度z是f(x,y)的值。
第三個座標系是一個二維的畫布,起點(0,0)在左上方。畫布上任意點的座標(sx,sy),我們使用等角投影將三維點(x,y,z)投影到二維的畫布中。畫布上的點離右邊越遠,x和y值越大,z值越小。x和y的垂直縮放係數是30度角的sin值,水平縮放系統是30度角的cos值。z的縮放係數0.4是一個任意的值。
對於二維網格中的每一個網格單元,main函數會計算該單元在畫布上對應的多邊形ABCD的頂點,B對應頂點(i,j),A、C、D是B的鄰接點,然後輸出SVG的繪製指令。
文章所有權:Golang隱修會 連絡人:孫飛,CTO@188.com!