你如何处理 golang 中的 float64 比较?
Posted
技术标签:
【中文标题】你如何处理 golang 中的 float64 比较?【英文标题】:How do you handle float64 comparisons in golang? 【发布时间】:2019-04-23 16:29:01 【问题描述】:我正在浏览 go 并且在“练习:循环和函数”中遇到 float64 比较问题,您可以在其中编写一个函数来确定平方根。
来自示例: 计算机通常使用循环计算 x 的平方根。从猜测 z 开始,我们可以根据 z² 与 x 的接近程度来调整 z,从而产生更好的猜测:
z -= (z*z - x) / (2*z)
我编写了一个函数,它会继续更新 z 直到值停止变化。只有 float64 比较永远不会失败,这会导致无限循环。解决这些类型问题的一种方法是四舍五入,但我不确定如何在不使用数学模块的情况下在 golang 中做到这一点。
你如何在 golang 中对 float64 数字进行四舍五入?在 golang 中比较浮点数的标准方法是什么?
package main
import (
"fmt"
)
func Sqrt(x float64) float64
// Need to look into float rounding in go
z := 1.0
zprev := 0.01
for z != zprev
zprev = z
z -= (z*z - x) /(2*z)
fmt.Printf("z: %g\nzprev: %g\n", z, zprev)
fmt.Println("_________________________________________")
fmt.Println("Finished")
return z
func main()
fmt.Println(Sqrt(2))
输出:
z: 1.5
zprev: 1
_________________________________________
z: 1.4166666666666667
zprev: 1.5
_________________________________________
z: 1.4142156862745099
zprev: 1.4166666666666667
_________________________________________
z: 1.4142135623746899
zprev: 1.4142156862745099
_________________________________________
z: 1.4142135623730951
zprev: 1.4142135623746899
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________
z: 1.4142135623730951
zprev: 1.414213562373095
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________
z: 1.4142135623730951
zprev: 1.414213562373095
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________
在一个点之后,z 和 zprev 继续在两个仅相差一个精度点(1.414213562373095 和 1.4142135623730951)的值之间无限地交替
【问题讨论】:
为什么不想使用math
包?这就是浮点舍入函数的所在。
另一种方法是将绝对差异与给定阈值进行比较......但Abs
函数也在math
包中。
练习题是在不使用数学包的情况下实现平方根函数,所以我想保持一致。这真的只是一个学习练习,因为我刚刚第一次使用 golang
比较浮点数的标准方法是你已经在做的,标准的四舍五入方法是使用math.Round
。它猜想这个练习是让你弄清楚如何在没有 Go 开发人员通常可用的标准工具的情况下做到这一点。
如果你想要一个没有math
包的舍入函数,请参阅Golang Round to Nearest 0.05。如果你想要一个“专门的”abs()
函数(满足你的需要),那是一个微不足道的if
声明......
【参考方案1】:
取您要比较的两个数字之间的差值,而不是四舍五入,并检查它是否在-epsilon
和epsilon
之间,其中epsilon
是您认为足够小的差值。
注意:不可靠的相等比较不是特定于 go 的;这是浮点数的普遍问题。
【讨论】:
【参考方案2】:这就是我的做法——假设你根本不想使用 math
包。
package main
import "fmt"
func abs(x float64) float64
if x < 0
return -x
return x
func Sqrt(x float64) float64
z := x
var zprev float64
for abs(zprev-z) > 1e-6
zprev, z = z, z-(z*z-x)/(2*z)
return z
func main()
fmt.Println(Sqrt(2))
输出:
1.4142135623730951
【讨论】:
【参考方案3】:我发现解决这个问题的方法是将比较中的一个值添加到比较的两侧。
下面我在比较的两边都添加了 z,现在比较可以正常工作了。
package main
import (
"fmt"
)
func Sqrt(x float64) float64
// Need to look into float rounding in go
z := 1.0
zprev := 0.01
for zprev + z != z + z
zprev = z
z -= (z*z - x) /(2*z)
fmt.Printf("z: %g\nzprev: %g\n", z, zprev)
fmt.Println("_________________________________________")
fmt.Println("Finished")
return z
输出:
z: 1.5
zprev: 1
_________________________________________
z: 1.4166666666666667
zprev: 1.5
_________________________________________
z: 1.4142156862745099
zprev: 1.4166666666666667
_________________________________________
z: 1.4142135623746899
zprev: 1.4142156862745099
_________________________________________
z: 1.4142135623730951
zprev: 1.4142135623746899
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________
z: 1.4142135623730951
zprev: 1.414213562373095
_________________________________________
Finished
1.4142135623730951
【讨论】:
这不是一个可靠的解决方案!它恰好在您的示例中有效,但在其他情况下会失败。 一般来说,我建议不要在没有理解它应该始终有效(或者,如在这种情况下,意识到它不应该)。尽管zprev
和z
不相等,为什么zprev + z
和z + z
通常应该相等?他们不会:这一次它起作用了,因为它恰好消除了 zprev
和 z
之间的微小差异,因为浮点运算固有的不准确性。
其他初始x
值将导致一对不同的zprev
和z
,在相加后可能碰巧保持不相等。【参考方案4】:
Lance,我相信您现在一定已经精通编程,但对于像我这样仍然从这里开始的其他人来说,是 @Aasmund Eldhuset 建议的浮动比较。
package main
import (
"fmt"
)
func Sqrt(x float64) float64
z:=float64(x/2)
i:=1
oldz := -1.0
for
fmt.Printf(" %d => %f\n",(i),z)
z-=(z*z-x)/(2*z)
if diff:=(oldz-z); diff>-0.0000000001 && diff<0.0000000001
fmt.Println("Breaking!")
break
oldz = z
i+=1
return z
func main()
fmt.Println(Sqrt(2))
输出:
1 => 1.000000
2 => 1.500000
3 => 1.416667
4 => 1.414216
5 => 1.414214
Breaking!
1.4142135623730951
【讨论】:
以上是关于你如何处理 golang 中的 float64 比较?的主要内容,如果未能解决你的问题,请参考以下文章