go語言中decimal的用法詳解
decimal是為了解決Golang中浮點數(shù)計算時精度丟失問題而生的一個庫,使用decimal庫我們可以避免在go中使用浮點數(shù)出現(xiàn)精度丟失的問題。
github地址:https://github.com/shopspring/decimal
1. 精度丟失的case
func TestFloat(t *testing.T) { a := 1100.1 b := a * 100 fmt.Println(b) // should be: 110010 output: 110009.99999999999 }
上面的精度就發(fā)生了丟失,浮點數(shù)的位數(shù)雖然我們看到的是1100.1,但對于浮點數(shù)的計算而言卻是無限接近于1,但與1不等。
當(dāng)我們需要對float的數(shù)據(jù)進(jìn)行計算的時候,我們可以使用第三方的decimal包來解決這個問題。
2. decimal的應(yīng)用場景
decimal的應(yīng)用場景主要出現(xiàn)在對float浮點數(shù)進(jìn)行加減乘除操作的時候,尤其是對于銀行金融一塊的業(yè)務(wù),如果精度丟失,一筆交易上面的損失可以忽略不計,但當(dāng)交易的規(guī)模達(dá)到幾千萬或者億甚至幾十億的時候,這個時候的損失就會大的嚇人了。
3. 使用decimal
使用decimal第一步是引入這個包,decimal,在官方的描述中,這個包的功能描述如下:
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.
go中任意精度定點的十進(jìn)制數(shù)
注意:十進(jìn)制庫"只能"表示小數(shù)點后最多2^31位的數(shù)字。
小數(shù)點后2^31位數(shù)字,對于絕大多數(shù)的項目精度要求是足夠的,簡而言之,decimal可以解決我們絕大多數(shù)的浮點數(shù)精度計算場景。
對于上面精度丟失的case,當(dāng)我們引入decimal包之后,我們可以得到正確的結(jié)果。
func TestDecimalOne(t *testing.T){ a := 1100.1 b := 100 d := decimal.NewFromFloat(a) result := d.Mul(decimal.NewFromInt(int64(b))) fmt.Println(result) // should be: 110010, output: 110010 }
計算結(jié)果的精度沒有丟失,計算結(jié)果正確。
4. decimal其他實用的場景
使用decimal的時候,切記浮點數(shù)計算所有數(shù)據(jù)的初始化必須通過decimal進(jìn)行,否則還是會導(dǎo)致精度的丟失,為什么這么說呢,看看下面的例子你就明白了。
import ( ?? ?"fmt" ?? ?"github.com/shopspring/decimal" ?? ?"testing" ) func TestDecimal(t *testing.T) { ?? ?x := 0.28 ?? ?// this will cause the multi result has error ?? ?// output should be 28, but actually 28.000000000000004(error) ?? ?errorMul := decimal.NewFromFloat(x * 100).String() }
上面的case就是因為100沒有使用decimal進(jìn)行初始化導(dǎo)致最后計算的精度被擴(kuò)大了。正確的處理方式如下:
import ( ?? ?"fmt" ?? ?"github.com/shopspring/decimal" ?? ?"testing" ) func TestDecimal(t *testing.T) { ?? ?x := 0.28 ?? ?// the correct operate number multi is use the decimal Mul method. ?? ?correctMul := decimal.NewFromFloat(x).Mul(decimal.NewFromInt(100)).String() ? ? fmt.Println(correctMul) // output: 28 }
4.1 獲取結(jié)果的整數(shù)部分
使用IntPart可以獲取到浮點數(shù)計算結(jié)果的整數(shù)部分。
func TestDecimalTwo(t *testing.T){ a := 0.01234 b := 100 // 0.01234 * 100 = 1.234, int part=1, output=1 fmt.Println(decimal.NewFromFloat(a).Mul(decimal.NewFromInt(int64(b))).IntPart()) }
4.2 小數(shù)點后填充
使用IntPart可以對計算后的數(shù)據(jù)進(jìn)行小數(shù)點位數(shù)的補零填充。
func TestDecimalThree(t *testing.T){ a := 0.012 b := 100 x := decimal.NewFromFloat(a).Mul(decimal.NewFromInt(int64(b))) // 小數(shù)點后的位數(shù)填充, 0.012*100=1.2, 填充3位,因為已經(jīng)有一位了,填充兩個0, 1.200 fmt.Println(x.StringFixed(3)) }
4.3 比較數(shù)字的大小
浮點數(shù)的比較decimal提供了一些比較有用的函數(shù)方法,這里列舉了一部分。
import ( ?? ?"fmt" ?? ?"github.com/shopspring/decimal" ?? ?"testing" ) func TestDecimalFour(t *testing.T) { ?? ?a := decimal.NewFromFloat(-1.11) ?? ?b := decimal.NewFromInt(3) ?? ?c, _ := decimal.NewFromString("2.023") ?? ?// 是否是負(fù)數(shù) ?? ?fmt.Println(a.IsNegative()) ?? ?// 取絕對值 ?? ?fmt.Println(a.Abs()) ?? ?// 比較是否相等 ?? ?fmt.Println(a.Equal(b)) ?? ?// 比較小于 ?? ?fmt.Println(a.LessThan(b)) ?? ?// 比較小于等于 ?? ?fmt.Println(a.LessThanOrEqual(b)) ?? ?// 比較大于等于 ?? ?fmt.Println(b.GreaterThanOrEqual(a)) ?? ?// 是否是0 ?? ?fmt.Println(c.IsZero()) }
5 小結(jié)
decimal對于浮點數(shù)的計算提供了極大的便利性,讓我們在使用浮點數(shù)進(jìn)行大小計算的時候不用擔(dān)心精度丟失的問題,尤其是對于金融行業(yè),精度丟失造成資損就是很重大的生產(chǎn)事故了。
對decimal有一些基本的了解,當(dāng)我們在工作中有場景需要使用到浮點數(shù)的計算的時候,可以直接使用decimal來幫助我們快速完善計算的邏輯。
到此這篇關(guān)于go語言中decimal的用法詳解的文章就介紹到這了,更多相關(guān)go語言decimal用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言實現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗功能
這篇文章主要介紹了go語言實現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04實時通信的服務(wù)器推送機(jī)制 EventSource(SSE) 簡介附go實現(xiàn)示例代碼
EventSource是一種非常有用的 API,適用于許多實時應(yīng)用場景,它提供了一種簡單而可靠的方式來建立服務(wù)器推送連接,并實現(xiàn)實時更新和通知,這篇文章主要介紹了實時通信的服務(wù)器推送機(jī)制 EventSource(SSE)簡介附go實現(xiàn)示例,需要的朋友可以參考下2024-03-03