Golang中浮点数减法运算问题解析

Golang中浮点数减法运算问题解析

package main

import (
    "fmt"
)

type Order struct {
    amount, price, fee float64
}

const (Fee = 0.000141)

func main() {
    a := Order{
        price:  0.5,
        amount: 0.008,
    }
    a.amount -= Fee * 0.0001
    fmt.Println(a.amount)
}

各位Gopher大家好,希望你们都一切顺利。

我正在开发一个金融项目,这里有一个存储用户余额的float64类型变量,但是当我进行余额减法运算时,它返回的值比预期要低,我想解决这个问题。

预期结果应该是 = 0.0079999859 但Go编译器返回 = 0.007999985900000001

有没有办法用float64类型来解决这个问题?

链接:https://play.golang.org/p/FBHkx2qxsJQ


更多关于Golang中浮点数减法运算问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

18 回复

好的,我知道了。

更多关于Golang中浮点数减法运算问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是否解决了浮点数精度和精确值的问题?

让我用这种方式检查一下
顺便感谢 <3

@jayts 感谢,我正在处理这个问题

编写一个使用该包的单元测试,然后找出答案。

我应该使用 int 还是 big.Float?

一种你选择的定点数据类型,能够表示预期域中的任何值。

我不知道这个方法有多实用,但它能完成任务。

fmt.Printf("%.10f",a.amount)

我曾使用这个包进行浮点数到十进制数的转换,效果很好。https://github.com/shopspring/decimal

感谢 @geosoft1 的帮助和评论,但我需要在计算中使用浮点数,因此这种方法无法在此处使用,它只能用于打印数字的小数位数。

taalhach:

有没有办法用 float64 解决这个问题?

不行。

浮点数根本无法精确表示数值,这就是为什么我们多次建议不要在处理金融相关事务时使用浮点数类型。

我确实尝试过这个方法,但四舍五入的问题影响了我的实现。

所以现在我改用int64类型,并使用货币单位来处理,而不是直接使用大数值。

感谢你总是提供最佳建议。

我开发了一个应用程序,它使用整数值来存储数据。根据需求,只有4位小数被视为有效小数位。因此,所有操作都在整数值上执行,而展示时则显示为2位小数。

这就是我现在的做法:我把float64改成了用int64表示金额单位,现在运行得很好。不过在将数据存入数据库或从服务器向客户端发送值时,我需要用这些值除以总数。

// 代码示例

一个简单但可能较慢的解决方案可以是这样的函数:

func floatPrecision(n float64, precision int) float64 {
	format:=fmt.Sprintf("%%.%df",precision)
	r,_:=strconv.ParseFloat(fmt.Sprintf(format,n),64)
	return r
}

这很可能会让情况变得更糟。

精度为1的1.23可能会被解析为1.19,因为它显示为1.2,而在解析浮点数时,IEEE规范要求必须将其解析为可表示的下一个较小数字。

如果你正在使用浮点数,除了不使用浮点数之外,没有其他方法可以避免舍入问题。

你好,

作为你编程项目的一部分,我建议你暂停编码,在继续之前仔细研究这个问题。(特别是如果这是用于实际业务用途,而不仅仅是个人的项目或实验。)这个问题没有简单的答案,你需要先研究和思考你的选择与需求,再开发代码。

据我所知,处理财务计算的最佳方式是使用支持十进制运算而非二进制运算的语言。曾经,这意味着使用COBOL,这也是COBOL如此受欢迎且寿命长久的最重要原因之一。

即使在今天,这仍然是一个复杂的议题。以下是我找到的一些网页,可能帮助你入门:

英特尔网站上关于此问题的简短讨论:https://software.intel.com/en-us/blogs/2008/03/06/intel-decimal-floating-point-math-library/

维基百科关于十进制浮点数的介绍:https://en.wikipedia.org/wiki/Decimal_floating_point

关于在Go中引入十进制类型的提案讨论:

Issue: proposal: math/big: Decimal

Issue: proposal: math/decimal: add decimal128 implementation

我还找到了一些你可以了解的库:

apd: An Arbitrary-Precision Decimal Package for Go

shopspring/decimal

rin01/decnum

做到“正确”可能会很复杂且繁琐。

免责声明:我不推荐任何东西,这超出了我通常的专业领域,所以请不要将我写的内容视为权威。

在金融应用中,浮点数精度问题是一个常见挑战。由于浮点数在内存中采用二进制表示,某些十进制小数无法精确表示,导致精度误差。以下是几种解决方案:

方案1:使用decimal包(推荐)

package main

import (
    "fmt"
    "github.com/shopspring/decimal"
)

type Order struct {
    amount, price, fee float64
}

const Fee = 0.000141

func main() {
    // 使用decimal进行精确计算
    amount := decimal.NewFromFloat(0.008)
    fee := decimal.NewFromFloat(Fee).Mul(decimal.NewFromFloat(0.0001))
    result := amount.Sub(fee)
    
    fmt.Printf("精确结果: %s\n", result.String())
    fmt.Printf("浮点数结果: %v\n", result.InexactFloat64())
}

方案2:格式化输出控制精度

package main

import (
    "fmt"
)

func main() {
    a := 0.008
    fee := 0.000141 * 0.0001
    result := a - fee
    
    // 格式化输出到指定精度
    fmt.Printf("格式化结果: %.10f\n", result)
}

方案3:使用整数运算

package main

import (
    "fmt"
)

type Order struct {
    amount, price, fee int64 // 使用整数表示,例如乘以1e8
}

const (
    Fee = 141 // 0.000141 * 1e6
    Scale = 1e8
)

func main() {
    a := Order{
        amount: int64(0.008 * Scale),
        price:  int64(0.5 * Scale),
    }
    
    // 整数运算:0.000141 * 0.0001 = 0.0000000141
    feeAdjustment := int64(Fee * 0.0001 * Scale)
    a.amount -= feeAdjustment
    
    // 转换回浮点数
    result := float64(a.amount) / Scale
    fmt.Printf("整数运算结果: %.10f\n", result)
}

方案4:自定义精度处理函数

package main

import (
    "fmt"
    "math"
)

func roundFloat(val float64, precision uint) float64 {
    ratio := math.Pow(10, float64(precision))
    return math.Round(val*ratio) / ratio
}

func main() {
    a := 0.008
    fee := 0.000141 * 0.0001
    result := a - fee
    
    // 四舍五入到指定精度
    rounded := roundFloat(result, 10)
    fmt.Printf("四舍五入结果: %.10f\n", rounded)
}

对于金融应用,强烈推荐使用 github.com/shopspring/decimal 包,它专门为精确的十进制运算设计,能有效避免浮点数精度问题。

回到顶部