您如何处理golang的浮动64比较?

您如何处理golang的浮动64比较?,go,Go,我正在进行围棋之旅,在“练习:循环和函数”中的float64比较中遇到了一个问题,在这里,您编写了一个函数来确定平方根 例如: 计算机通常使用循环计算x的平方根。从一些猜测z开始,我们可以根据z²与x的接近程度调整z,从而产生更好的猜测: z -= (z*z - x) / (2*z) 我编写了一个函数,它将继续更新z,直到值停止更改。只有float64比较从不失败,这会导致无限循环。解决这类问题的一种方法是四舍五入,但我不确定在golang中不使用数学模块怎么做 如何对golang中的浮点数进

我正在进行围棋之旅,在“练习:循环和函数”中的float64比较中遇到了一个问题,在这里,您编写了一个函数来确定平方根

例如: 计算机通常使用循环计算x的平方根。从一些猜测z开始,我们可以根据z²与x的接近程度调整z,从而产生更好的猜测:

z -= (z*z - x) / (2*z)
我编写了一个函数,它将继续更新z,直到值停止更改。只有float64比较从不失败,这会导致无限循环。解决这类问题的一种方法是四舍五入,但我不确定在golang中不使用数学模块怎么做

如何对golang中的浮点数进行四舍五入,比较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: 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
1.4142135623730951
 1 => 1.000000
 2 => 1.500000
 3 => 1.416667
 4 => 1.414216
 5 => 1.414214
Breaking!
1.4142135623730951

在点z和zprev继续在两个仅关闭一个精度点(1.414213562373095和1.4142135623730951)的值之间无限期交替后

而不是取整,取要比较的两个数字之间的差值,并检查其是否在
-epsilon
epsilon
之间,<代码>ε< /代码>是任何你认为是一个非常小的差别。


注:不可靠的相等性比较不是特定的;浮点数是一个普遍存在的问题。

我发现解决这个问题的方法是将比较中的一个值添加到比较的两侧

下面我在比较的两侧都添加了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
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________
z: 1.4142135623730951
zprev: 1.414213562373095
_________________________________________
z: 1.414213562373095
zprev: 1.4142135623730951
_________________________________________

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
1.4142135623730951
 1 => 1.000000
 2 => 1.500000
 3 => 1.416667
 4 => 1.414216
 5 => 1.414214
Breaking!
1.4142135623730951

下面是我的做法——假设你根本不想使用
math
软件包

主程序包
输入“fmt”
func abs(x float64)float64{
如果x<0{
返回-x
}
返回x
}
func Sqrt(x float64)float64{
z:=x
var zprev浮动64
对于abs(zprev-z)>1e-6{
zprev,z=z,z-(z*z-x)/(2*z)
}
返回z
}
func main(){
格式打印(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: 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
1.4142135623730951
 1 => 1.000000
 2 => 1.500000
 3 => 1.416667
 4 => 1.414216
 5 => 1.414214
Breaking!
1.4142135623730951

兰斯,我相信到现在为止,你一定已经对编程非常熟悉了,但是对于像我这样仍然在这里起步的其他人来说,@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))
}

您为什么不想使用
math
软件包?这就是浮点舍入函数的位置。另一种方法是将绝对差值与给定阈值进行比较。。。但是
Abs
函数也在
math
包中。实践问题是在不使用math包的情况下实现平方根函数,因此我希望保持一致。这真的只是一个学习练习,因为我刚刚第一次使用golang比较浮动的标准方法是你已经在做的事情,对它们进行取整的标准方法是使用
math.round
。我猜这个练习是为了让你知道如何在没有围棋开发人员通常可用的标准工具的情况下完成。如果你想要一个没有
math
包的舍入函数,请参阅。如果您想要一个“专用的”
abs()
函数(适合您的需要),这是一个微不足道的
If
语句……这不是一个可靠的解决方案!在你的例子中,它正好起作用,但在其他情况下会失败。一般来说,我建议不要尝试一些武断的事情,如果它“似乎起作用”,而没有理解为什么它应该一直起作用(或者,在本例中,意识到它不应该起作用)的根本原因。为什么
zprev+z
z+z
通常应该相等,尽管
zprev
z
不相等?它们不会:这一次起作用了,因为碰巧加法消除了
zprev
z
之间的微小差异,这是由于浮点运算固有的不精确性。其他初始
x
值将导致一对不同的
zprev
z
,在加法后可能会保持不相等。