Golang使用Decimal库避免运算中精度损失详细步骤

 更新时间:2023年06月15日 15:26:41   作者:eRain  
decimal是为了解决Golang中浮点数计算时精度丢失问题而生的一个库,使用decimal库我们可以避免在go中使用浮点数出现精度丢失的问题,下面这篇文章主要给大家介绍了关于Golang使用Decimal库避免运算中精度损失的相关资料,需要的朋友可以参考下

前言

我们在项目中涉及到数值计算时,如果直接使用golang的运算符,会造成精度的损失,例如:

a := 1136.1
b := a * 100
fmt.Println(b) // 正确结果应该是 113610 但输出 113609.99999999999

c := 1.7
fmt.Println(a - c) // 正确结果应该是 1134.4 但输出 1134.3999999999999
fmt.Println(b - c) // 正确结果应该是 113608.3 但输出 113608.29999999999

那么如何避免这种情况呢?golang中没有提供对于精度运算相应的包。这里需要用到第三方的decimal包了。

一、Decimal库是什么?

是一个第三方提供的用于go程序数值计算时避免精度损失的包,引用官方的描述:

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库“只能”表示小数点后最多2^31位的数字。当然,这对于绝大部分项目来说足够了。

二、使用步骤

1.引入库

下载包:

go get github.com/shopspring/decimal

go代码中引入包:

import "github.com/shopspring/decimal"

2.Decimal库的使用

使用decimal后,上面例子的代码就应该这样写:

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
}

之后对于各种数字的计算就会变得得心应手:

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)

	// 对于保留小数的处理
	pi := decimal.NewFromFloat(3.1415926535897932384626)
	pi1 := pi.Round(3)    // 对pi值四舍五入保留3位小数
	fmt.Println(pi1)       // 3.142
	pi2 := pi.Truncate(3) // 对pi值保留3位小数之后直接舍弃
	fmt.Println(pi2)       // 3.141
}

对于数值变量类型的转换也游刃有余了:

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() // 舍去小数取整
fmt.Println(c)  // int64 69

b = decimal.NewFromInt(c).String()
fmt.Println(b) // string 69

c = d.Round(0).IntPart() // 不保留小数四舍五入取整
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

总结

Decimal库会给我们项目中用到数值计算时提供极大的便利和安全性。

最后需要注意的一点是,使用Decimal库的变量数据类型全部为decimal.Decimal,同样decimal.Decimal也可以作为声明变量时的数据类型使用,所以记得在最后做变量赋值时转换为需要的数据类型。

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
}

到此这篇关于Golang使用Decimal库避免运算中精度损失的文章就介绍到这了,更多相关Golang避免运算精度损失内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang根据生日计算星座和属相实例

    golang根据生日计算星座和属相实例

    这篇文章主要为大家介绍了golang根据生日计算星座和属相的示例代码,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go语言基础变量的声明及初始化示例详解

    Go语言基础变量的声明及初始化示例详解

    这篇文章主要为大家介绍了Go语言基础变量的声明及初始化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助祝大家多多进步,早日升职加薪
    2021-11-11
  • Golang基于Vault实现敏感数据加解密

    Golang基于Vault实现敏感数据加解密

    数据加密是主要的数据安全防护技术之一,敏感数据应该加密存储在数据库中,降低泄露风险,本文将介绍一下利用Vault实现敏感数据加解密的方法,需要的可以参考一下
    2023-07-07
  • Golang中自定义json序列化时间格式的示例代码

    Golang中自定义json序列化时间格式的示例代码

    Go语言作为一个由Google开发,号称互联网的C语言的语言,自然也对JSON格式支持很好,下面这篇文章主要介绍了关于Golang中自定义json序列化时间格式的相关内容,下面话不多说了,来一起看看详细的介绍吧
    2024-08-08
  • GO 使用Webhook 实现github 自动化部署的方法

    GO 使用Webhook 实现github 自动化部署的方法

    这篇文章主要介绍了GO 使用Webhook 实现github 自动化部署的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 重学Go语言之如何开发RPC应用

    重学Go语言之如何开发RPC应用

    这篇文章主要为大家详细介绍了在Go语言中如何构建RPC应用,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-09-09
  • GO语言中的Map使用方法详解

    GO语言中的Map使用方法详解

    这篇文章主要给大家介绍了关于GO语言中Map使用方法的相关资料,在go语言中map是散列表的引用,map的类型是map[k]v,也就是常说的k-v键值对,需要的朋友可以参考下
    2023-08-08
  • Go语言dolphinscheduler任务调度处理

    Go语言dolphinscheduler任务调度处理

    这篇文章主要为大家介绍了Go语言dolphinscheduler任务调度处理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Golang中互斥锁和读写互斥锁原理及示例代码

    Golang中互斥锁和读写互斥锁原理及示例代码

    在Golang中,互斥锁是一种基本的同步原语,用于实现对共享资源的互斥访问,读写互斥锁是一种特殊类型的互斥锁,它允许多个协程同时读取某个共享资源,本文将通过过示例代码详细介绍Golang中互斥锁和读写互斥锁,需要的朋友可以参考下
    2023-05-05
  • Go目录文件路径操作的实现

    Go目录文件路径操作的实现

    在Go语言中,可以使用绝对路径或相对路径来表示文件路径,本文就来介绍一下Go目录文件路径操作,感兴趣的可以了解一下
    2023-10-10

最新评论