如何在golang中处理float64比较?

I'm going through tour of go and am having a problem with float64 comparison in "Exercise: Loop and functions", where you write a function to determine the square root.

From example: Computers typically compute the square root of x using a loop. Starting with some guess z, we can adjust z based on how close z² is to x, producing a better guess:

z -= (z*z - x) / (2*z)

I wrote a function that continues to update z until the values stop changing. Only the float64 comparison never fails and this results in an infinite loop. One way to solve these types of issues is to round, but I'm not sure how to do that in golang without using the math module.

How do you round float64 numbers in golang and what is the standard way to compare floating point numbers in 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
zprev: %g
", z, zprev)
        fmt.Println("_________________________________________")


    }
    fmt.Println("Finished")
    return z
}


func main() {
    fmt.Println(Sqrt(2))
}

Output:

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
_________________________________________

After a point z and zprev continue to alternate between 2 values that are only off by one precision point (1.414213562373095 and 1.4142135623730951) indefinitely

Instead of rounding, take the difference between the two numbers you want to compare, and check that it is between -epsilon and epsilon, where epsilon is whatever you consider to be a sufficiently small difference.

Note: Unreliable equality comparisons are not go-specific; it's a universal problem with floating-point numbers.

I found that the solution to this problem was to add one of the values in the comparison to both sides of the comparison.

Below I have added z to both sides of the comparison and the comparison now works as expected.

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
zprev: %g
", z, zprev)
        fmt.Println("_________________________________________")

    }
    fmt.Println("Finished")
    return z
}

Output:

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

Here's how I'd do it – giving that you don't want to use the math package at all.

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))
}

Output:

1.4142135623730951