栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

Golang中的精度丢失问题及解决方案

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Golang中的精度丢失问题及解决方案

背景:

the actual total amount of batch 1191 is 58508228.05 but the Total Transaction Amount shows in admin is 58508228

数据库存储amount这个字段用的数据类型是decimal(20,4),现在需要计算的是sum(amount),并将其取出显示在admin上,由于sum(amount)的值可能较大,而golang语言中没有decimal这个数据类型,因此必然涉及到数据类型转换问题

bug来源:

一开始的思路:decimal → string → float32

record.Amount, err = strconv.ParseFloat(string(v["sum(amount)"]), 32)

浮点类型是不精确的类型,即便是float64也无法精确反映数据本身

原因:对于二进制小数,小数点右边能表达的值是 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 … 1/(2^n),也就是说计算机只能用这些 1/(2^n)之和来表达十进制的小数

例如:计算机表达十进制的 0.2 :

0.01 = 1/4 = 0.25 ,太大

0.001 =1/8 = 0.125 , 又太小

0.0011 = 1/8 + 1/16 = 0.1875 , 逼近0.2了

0.00111 = 1/8 + 1/16 + 1/32 = 0.21875 , 又大了

0.001101 = 1/8+ 1/16 + 1/64 = 0.203125 还是大

0.0011001 = 1/8 + 1/16 + 1/128 = 0.1953125 这结果不错

0.00110011 = 1/8+1/16+1/128+1/256 = 0.19921875
已经很逼近了, 就这样吧

而float32和float64的区别就在于,计算机提供的二进制位数不同,因为计算机不可能无限逼近,那么位数的作用就是在有限的位数内不断逼近十进制数

解决方案:

为了提供精确的值,最后选择"github.com/shopspring/decimal"这个包,decimal.NewFromString之所以可以精确转换小数,是因为它结合了big.int以及进制的巧妙精准转换

代码示例:

package main
 
import (
    "fmt"
    "github.com/shopspring/decimal"
    "strconv"
)
 
func main() {
    test01, err := strconv.ParseFloat("3840629.82", 32)
    test02, err := strconv.ParseFloat("3840629.82", 64)
    test03, err := decimal.NewFromString("3840629.82")
    test04, _ := test03.Float64()
 
    if err != nil {
        panic(err)
 
    }
 
    fmt.Printf("%fn", test01)
    fmt.Printf("%fn", test02)
    fmt.Printf("%vn", test03)
    fmt.Printf("%fn", test04)
 
}
 
 

参考:Golang 笔记之深入浮点数_weishixianglian的博客-CSDN博客_golang 浮点数

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/944198.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号