Reputation: 282
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\nzprev: %g\n", 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
Upvotes: 6
Views: 7591
Reputation: 76
Lance, I believe by now you must have been well versed with the programming but for others like me who are still starting here is the floating comparison as suggested by @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))
}
Output:
1 => 1.000000
2 => 1.500000
3 => 1.416667
4 => 1.414216
5 => 1.414214
Breaking!
1.4142135623730951
Upvotes: 0
Reputation: 1224
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
Upvotes: 2
Reputation: 282
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\nzprev: %g\n", 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
Upvotes: 0
Reputation: 37960
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.
Upvotes: 4