Golang保留两位小数精度丢失

Sprintf

四舍六入:

value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 9.824), 64)
fmt.Println(value) //9.82

value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", 9.826), 64)
fmt.Println(value) //9.83

第三位为5且5之后有有效数字,满足五入:

value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 9.8251), 64)
fmt.Println(value) //9.83

value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", 9.8351), 64)
fmt.Println(value) //9.84

第三位为5且5之后没有有效数字:
网上有人说,第二位为奇数则进位,第二位为偶数则舍去,例如:

value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 9.825), 64)
fmt.Println(value) //9.82

value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", 9.835), 64)
fmt.Println(value) //9.84

但是:

value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 9.815), 64)
fmt.Println(value) //9.81 居然舍去了

value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", 9.845), 64)
fmt.Println(value) //9.85 居然进位了

所以,如果想满足正常的四舍五入逻辑,最好不要使用Sprintf处理。

math.Trunc

fmt.Println(math.Trunc(9.815*1e2+0.5)*1e-2) //9.82
fmt.Println(math.Trunc(9.825*1e2+0.5)*1e-2) //9.83
fmt.Println(math.Trunc(9.835*1e2+0.5)*1e-2) //9.84
fmt.Println(math.Trunc(9.845*1e2+0.5)*1e-2) //9.85

以上结果显示符合四舍五入,但是偶尔会出现精度问题:

fmt.Println(math.Trunc(3.3*1e2+0.5)*1e-2) //3.3000000000000003
fmt.Println(math.Trunc(3.3000000000000003*1e2+0.5) * 1e-2) //3.3000000000000003

同样使用Trunc,稍作调整:

n10 := math.Pow10(2)
fmt.Println(math.Trunc((9.815+0.5/n10)*n10) / n10) //9.82
fmt.Println(math.Trunc((9.825+0.5/n10)*n10) / n10) //9.83
fmt.Println(math.Trunc((9.835+0.5/n10)*n10) / n10) //9.84
fmt.Println(math.Trunc((9.845+0.5/n10)*n10) / n10) //9.85
fmt.Println(math.Trunc((3.3+0.5/n10)*n10) / n10) //3.3
fmt.Println(math.Trunc((3.3000000000000003+0.5/n10)*n10) / n10) //3.3

符合四舍五入规则。

以上案例及解决方案转载自:点击链接跳转

但是 Trunc 调整后的方案依旧会出现 精度丢失问题

n10 := math.Pow10(2)
math.Trunc((129.975+0.5/n10)*n10) / n10 //结果为129.97

最终一番查找后的解决方案是 采用 decimal 包,将float64类型转换成 decimal 进行计算,需要自行下载 “github.com/shopspring/decimal”

附上我写的方法

func RetainTwoDecimal(num float64) float64 {
	var num2 float64
	decimalValue := decimal.NewFromFloat(0)
	if num <0 {
		decimalValue = decimal.NewFromFloat(num - 0.005)

	}else {
		decimalValue = decimal.NewFromFloat(num + 0.005)
	}
	//乘100
	decimalValue = decimalValue.Mul(decimal.NewFromInt(100))
	res,_ := decimalValue.Float64()
	num3 := math.Trunc(res)
	decimalValue2 := decimal.NewFromFloat(num3)
	//除100
	decimalValue2 = decimalValue2.Div(decimal.NewFromInt(100))
	num2,_ = decimalValue2.Float64()
	return num2
}