Golang使用Decimal庫避免運算中精度損失詳細(xì)步驟
前言
我們在項目中涉及到數(shù)值計算時,如果直接使用golang的運算符,會造成精度的損失,例如:
a := 1136.1 b := a * 100 fmt.Println(b) // 正確結(jié)果應(yīng)該是 113610 但輸出 113609.99999999999 c := 1.7 fmt.Println(a - c) // 正確結(jié)果應(yīng)該是 1134.4 但輸出 1134.3999999999999 fmt.Println(b - c) // 正確結(jié)果應(yīng)該是 113608.3 但輸出 113608.29999999999
那么如何避免這種情況呢?golang中沒有提供對于精度運算相應(yīng)的包。這里需要用到第三方的decimal包了。
一、Decimal庫是什么?
是一個第三方提供的用于go程序數(shù)值計算時避免精度損失的包,引用官方的描述:
Arbitrary-precision fixed-point decimal numbers in go.
Note: Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
注意這里有個Note:Decimal庫“只能”表示小數(shù)點后最多2^31位的數(shù)字。當(dāng)然,這對于絕大部分項目來說足夠了。
二、使用步驟
1.引入庫
下載包:
go get github.com/shopspring/decimal
go代碼中引入包:
import "github.com/shopspring/decimal"
2.Decimal庫的使用
使用decimal后,上面例子的代碼就應(yīng)該這樣寫:
package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { a := decimal.NewFromFloat(1136.1) b := a.Mul(decimal.NewFromInt(100)) fmt.Println(b) // 正確輸出 113610 c := decimal.NewFromFloat(1.7) fmt.Println(a.Sub(c)) // 正確輸出 1134.4 fmt.Println(b.Sub(c)) // 正確輸出 113608.3 }
之后對于各種數(shù)字的計算就會變得得心應(yīng)手:
package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { a := decimal.NewFromFloat(1.52) b := decimal.NewFromFloat(0.02) // 加減乘除運算 c := a.Add(b) // 1.52 + 0.02 = 1.54 d := a.Sub(b) // 1.52 - 0.02 = 1.5 e := a.Mul(b) // 1.52 * 0.02 = 0.0304 f := a.Div(b) // 1.52 / 0.02 = 76 fmt.Println(a, b, c, d, e, f) // 對于保留小數(shù)的處理 pi := decimal.NewFromFloat(3.1415926535897932384626) pi1 := pi.Round(3) // 對pi值四舍五入保留3位小數(shù) fmt.Println(pi1) // 3.142 pi2 := pi.Truncate(3) // 對pi值保留3位小數(shù)之后直接舍棄 fmt.Println(pi2) // 3.141 }
對于數(shù)值變量類型的轉(zhuǎn)換也游刃有余了:
var a float64 var b = "69.77" var c int64 d, err := decimal.NewFromString(b) if err != nil { fmt.Println(err.Error()) } a, _ = d.Float64() fmt.Println(a) // float64 69.77 c = d.IntPart() // 舍去小數(shù)取整 fmt.Println(c) // int64 69 b = decimal.NewFromInt(c).String() fmt.Println(b) // string 69 c = d.Round(0).IntPart() // 不保留小數(shù)四舍五入取整 fmt.Println(c) // int64 70
這里列出一些常用的方法:
n1 := decimal.NewFromFloat(-1.23) n2 := decimal.NewFromInt(3) n3, _ := decimal.NewFromString("0") n1.Abs() // 取絕對值 n1.Equal(n2) // n1 是否與 n2 相等 n1.LessThan(n2) // n1 是否小于 n2 n1.LessThanOrEqual(n2) // n1 是否小于或等于 n2 n1.GreaterThan(n2) // n1 是否大于 n2 n1.GreaterThanOrEqual(n2) // n1 是否大于或等于 n2 n3.IsZero() // n3 是否為0
總結(jié)
Decimal庫會給我們項目中用到數(shù)值計算時提供極大的便利和安全性。
最后需要注意的一點是,使用Decimal庫的變量數(shù)據(jù)類型全部為decimal.Decimal,同樣decimal.Decimal也可以作為聲明變量時的數(shù)據(jù)類型使用,所以記得在最后做變量賦值時轉(zhuǎn)換為需要的數(shù)據(jù)類型。
package main import ( "fmt" "github.com/shopspring/decimal" "reflect" ) func main() { n1 := decimal.NewFromFloat(3.14) var n2 string var n3 float64 var n4 int64 var n5 decimal.Decimal n2 = n1.String() n3, _ = n1.Float64() n4 = n1.IntPart() fmt.Printf("n1 = %v, type = %v\n", n1, reflect.TypeOf(n1).String()) // n1 = 3.14, type = decimal.Decimal fmt.Printf("n2 = %v, type = %v\n", n2, reflect.TypeOf(n2).String()) // n2 = 3.14, type = string fmt.Printf("n3 = %v, type = %v\n", n3, reflect.TypeOf(n3).String()) // n3 = 3.14, type = float64 fmt.Printf("n4 = %v, type = %v\n", n4, reflect.TypeOf(n4).String()) // n4 = 3, type = int64 fmt.Printf("n5 = %v, type = %v\n", n5, reflect.TypeOf(n5).String()) // n5 = 0, type = decimal.Decimal }
到此這篇關(guān)于Golang使用Decimal庫避免運算中精度損失的文章就介紹到這了,更多相關(guān)Golang避免運算精度損失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用golang實現(xiàn)一個MapReduce的示例代碼
這篇文章主要給大家介紹了關(guān)于如何使用golang實現(xiàn)一個MapReduce,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09Go?for-range?的?value值地址每次都一樣的原因解析
循環(huán)語句是一種常用的控制結(jié)構(gòu),在?Go?語言中,除了?for?關(guān)鍵字以外,還有一個?range?關(guān)鍵字,可以使用?for-range?循環(huán)迭代數(shù)組、切片、字符串、map?和?channel?這些數(shù)據(jù)類型,這篇文章主要介紹了Go?for-range?的?value值地址每次都一樣的原因解析,需要的朋友可以參考下2023-05-05