簡體   English   中英

你如何處理 golang 中的 float64 比較?

[英]How do you handle float64 comparisons in golang?

我正在瀏覽 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)的值之間無限期地交替

不要四舍五入,取要比較的兩個數字之間的差值,並檢查它是否在-epsilonepsilon之間,其中epsilon是您認為足夠小的差值。

注意:不可靠的相等比較不是 go-specific; 這是浮點數的普遍問題。

這就是我的做法——假定您根本不想使用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

我發現解決這個問題的方法是將比較中的一個值添加到比較的兩側。

下面我將 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

蘭斯,我相信你現在一定已經精通編程,但對於像我這樣仍在從這里開始的其他人來說,這是@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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM