Golang 處理浮點(diǎn)數(shù)遇到的精度問(wèn)題(使用decimal)
一、浮點(diǎn)數(shù)是什么?
浮點(diǎn)數(shù),是屬于有理數(shù)中某特定子集的數(shù)的數(shù)字表示,在計(jì)算機(jī)中用以近似表示任意某個(gè)實(shí)數(shù)。浮點(diǎn)數(shù)在計(jì)算機(jī)中主要用來(lái)表示小數(shù),浮點(diǎn)數(shù)就是小數(shù)點(diǎn)可以出現(xiàn)改變的數(shù)字。
因?yàn)樵谟?jì)算機(jī)的機(jī)器語(yǔ)言中,只有二進(jìn)制,機(jī)器語(yǔ)言只能識(shí)別0和1。所以,計(jì)算機(jī)也是不可能存儲(chǔ)小數(shù)的,所以需要有另一種變通的存儲(chǔ)方案。
這種方案就是:
1.指數(shù)方案
指數(shù)形式:其數(shù)值部分是一個(gè)小數(shù),小數(shù)點(diǎn)前的數(shù)字是零,小數(shù)點(diǎn)后的第一位數(shù)字不是零。一個(gè)實(shí)數(shù)可以有多種指數(shù)表示形式,但只有一種屬于標(biāo)準(zhǔn)化指數(shù)形式。
12.3 | 1.23*10^-1 |
1.23 | 1.23*10^0 |
1.23 | 0.123*10^1 |
上面的表格,我們可以很清晰的了解指數(shù)方案。同樣的我們也就能發(fā)現(xiàn)這樣表達(dá)小數(shù)會(huì)有一個(gè)嚴(yán)重的問(wèn)題,那就是指數(shù)表示形式太多了,如果不能約定好唯一形式,不同代碼之間溝通將會(huì)出現(xiàn)問(wèn)題。
2.規(guī)范化指數(shù)形式
在指數(shù)形式的多種表示方式中把小數(shù)部分中小數(shù)點(diǎn)前的數(shù)字為0、小數(shù)點(diǎn)后第1位數(shù)字不為0的表示形式稱為規(guī)范化的指數(shù)形式。
1.23 的規(guī)范化的指數(shù)形式 0.123*10^1
一個(gè)實(shí)數(shù)只有一個(gè)規(guī)范化的指數(shù)形式,在程序以指數(shù)形式輸出一個(gè)實(shí)數(shù)時(shí),必然以規(guī)范化的指數(shù)形式輸出。
0.123e001
1.為什么要以 0 開頭
1.23456要二進(jìn)制存放需分別存整數(shù)部和小數(shù)部,而0.123456則只需存小數(shù)部,這樣在占用相同字節(jié)的情況下,后一種方法可容納更大精度的浮點(diǎn)數(shù)。
2.為什么 e 后面要加 0 ,e001 和 e1 一樣嗎
后面加 0 是 %e 的輸出格式,并不是規(guī)范化的指數(shù)形式所必需的,
e001 和 e1 是一樣的
3.IEEE 754標(biāo)準(zhǔn)
由于不同機(jī)器對(duì)浮點(diǎn)數(shù)的表示有較大差別,這不利于軟件在不同計(jì)算機(jī)之間的移植。為此,美國(guó)IEEE提出了一個(gè)從系統(tǒng)角度支持浮點(diǎn)數(shù)的表示方法,稱為IEEE754標(biāo)準(zhǔn)(IEEE,1985),當(dāng)今流行的計(jì)算機(jī)幾乎都采用了這一標(biāo)準(zhǔn)。
IEEE 754規(guī)定了四種表示浮點(diǎn)數(shù)值的方式:?jiǎn)尉_度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實(shí)現(xiàn))。只有32位模式有強(qiáng)制要求,其他都是選擇性的。
二、出現(xiàn)精度問(wèn)題的情況
1.浮點(diǎn)數(shù)加減運(yùn)算
輸入數(shù)據(jù):
a = 2.3329 b = 3.1234
代碼如下(示例):
package main import "fmt" func main() { // a = 2.3329 b = 3.1234 a, b := 2.3329, 3.1234 c := a + b fmt.Println(c) //5.456300000000001} }
結(jié)果精度出現(xiàn)問(wèn)題
2.3329 + 3.1234 = 5.456300000000001
已經(jīng)出錯(cuò)
2.float64與float32之間轉(zhuǎn)換
輸入數(shù)據(jù):
a = 9.99999
代碼如下(示例):
package main import "fmt" func main() { var a float32 a = 9.99999 b := float64(a) fmt.Println(b) //9.999990463256836} }
結(jié)果精度出現(xiàn)問(wèn)題
9.99999 = 9.999990463256836
已經(jīng)出錯(cuò)
3.int64和float64,int32和float32轉(zhuǎn)換
1.int32和float32轉(zhuǎn)換
輸入數(shù)據(jù):
a = 9.99999
代碼如下(示例):
package main import "fmt" func main() { var a int32 a = 999990455 b := float32(a) fmt.Printf("%f\n", b) //999990464.000000} }
結(jié)果精度出現(xiàn)問(wèn)題
999990455= 999990464.000000
已經(jīng)出錯(cuò)
2.int64和float64轉(zhuǎn)換
輸入數(shù)據(jù):
a = 999999942424527242
代碼如下(示例):
package main import "fmt" func main() { var a int64 a = 999999942424527242 b := float64(a) fmt.Printf("%f\n", b) //999999942424527232.000000} }
結(jié)果精度出現(xiàn)問(wèn)題
999999942424527242 = 999999942424527232.000000
已經(jīng)出錯(cuò)
4.float64位直接乘100
輸入數(shù)據(jù):
a = 999999942424527242
代碼如下(示例):
package main import "fmt" func main() { var a float64 a = 1128.61 b := a * 100 fmt.Println(b) //112860.99999999999} }
結(jié)果精度出現(xiàn)問(wèn)題
1128.61 * 100= 112860.99999999999
已經(jīng)出錯(cuò)
三、decimal 解決精度問(wèn)題
利用 Decimal 包解決精度問(wèn)題
go get github.com/shopspring/decimal
1.浮點(diǎn)數(shù)加減運(yùn)算
輸入數(shù)據(jù):
a = 2.3329 b = 3.1234
代碼如下(示例):
package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { // a = 2.3329 b = 3.1234 a, b := 2.3329, 3.1234 c := decimal.NewFromFloat(a) d := decimal.NewFromFloat(b) fmt.Println(a, b) fmt.Println(c, d) fmt.Println("此時(shí)ab 與 cd 相同") fmt.Println(a + b) //5.456300000000001} fmt.Println(c.Add(d)) //5.4563} }
結(jié)果精度不再出現(xiàn)問(wèn)題
2.float64與float32之間轉(zhuǎn)換
輸入數(shù)據(jù):
a = 9.99999
代碼如下(示例):
package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { var a float32 a = 9.99999 c := decimal.NewFromFloat32(a) b := float64(a) c.Float64() fmt.Println(b) //9.999990463256836} fmt.Println(c.Float64()) //9.99999} }
結(jié)果精度不再出現(xiàn)問(wèn)題
3.float64位直接乘100
輸入數(shù)據(jù):
a = 999999942424527242
代碼如下(示例):
package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { var a float64 a = 1128.61 c := decimal.NewFromFloat(a) b := a * 100 fmt.Println(b) //112860.99999999999} fmt.Println(c.Mul(decimal.NewFromInt(100))) //112861} }
結(jié)果精度不再出現(xiàn)問(wèn)題
總結(jié)
通過(guò)以上兩個(gè)例子,我們已經(jīng)了解了浮點(diǎn)數(shù)的精度問(wèn)題,那么在工作中,我們就需要更換運(yùn)算方式。我們需要盡量選擇Decimal,否則如果使用 Float 出現(xiàn)精度問(wèn)題之后,到那時(shí)再更換方法,既費(fèi)時(shí)又費(fèi)力,能剛開始就解決,何樂而不為呢。
到此這篇關(guān)于Golang 處理浮點(diǎn)數(shù)遇到的精度問(wèn)題(使用decimal)的文章就介紹到這了,更多相關(guān)Golang 浮點(diǎn)精度內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang初始化MySQL數(shù)據(jù)庫(kù)方法淺析
這篇文章主要介紹了Golang初始化MySQL數(shù)據(jù)庫(kù)的方法,數(shù)據(jù)庫(kù)的建立第一步即要初始化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-05-05GoLand編譯帶有構(gòu)建標(biāo)簽的程序思路詳解
這篇文章主要介紹了GoLand編譯帶有構(gòu)建標(biāo)簽的程序,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11go?goquery網(wǎng)頁(yè)解析實(shí)現(xiàn)示例
這篇文章主要為大家介紹了go?goquery網(wǎng)頁(yè)解析實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08GoLang中sql.Exec()報(bào)錯(cuò)解決辦法
這篇文章主要給大家介紹了關(guān)于GoLang中sql.Exec()報(bào)錯(cuò)的解決辦法,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01Golang實(shí)現(xiàn)Redis過(guò)期時(shí)間實(shí)例探究
這篇文章主要介紹了Golang實(shí)現(xiàn)Redis過(guò)期時(shí)間實(shí)例探究,2024-01-01提升Go語(yǔ)言開發(fā)效率的小技巧實(shí)例(GO語(yǔ)言語(yǔ)法糖)匯總
這篇文章主要介紹了提升Go語(yǔ)言開發(fā)效率的小技巧匯總,也就是Go語(yǔ)言的語(yǔ)法糖,掌握好這些可以提高我們的開發(fā)效率,需要的朋友可以參考下2022-11-11