Golang中如何判断数值是否发生微小变化
Golang中如何判断数值是否发生微小变化 在练习《Go语言之旅》中的这个示例时遇到问题:
“接下来,修改循环条件,使得当值停止变化(或仅发生极小变化)时循环终止。”
v1 := 1.7320508075688772
v2 := 1.7320508075688774
如何判断v1和v2之间的差异是否属于极小变化?
6 回复
好的,感谢你的问题 😊 让问题变得简单了:
如果 v2 - v1 >= 0.0 && v2 - v1 <= 0.00001 { // … }
if v2 - v1 >= 0.0 && v2 - v1 <= 0.00001 {
// …
}
但如果 v2 - v1 < 0.0 呢?
常见的做法是检查 math.Abs(v2 - v1) <= epsilon,其中 epsilon 是允许的最大差值,超出此值则视为"明显变化",在您的情况下该值为 0.00001。
math.Abs(v2 - v1) <= epsilon
感谢您的提示。我原本以为在所用算法方面,v2版本应该总是优于v1版本。
无论如何,我已经根据您的建议重写了代码:
package main
import (
"fmt"
"math"
)
// 确定微小变化量
const epsilon = 0.000001
func Sqrt(x float64) float64 {
z := x / 2
z2 := z
var diff float64
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
diff = z2 - z
// 替代 math.Abs(diff)
if diff < 0 {
diff *= -1
}
fmt.Println(i+1, "; z=", z, "; z2 - z=", z2, "-", z, "=", diff)
if diff <= epsilon {
break
}
z2 = z
}
return z
}
func main() {
x := 3.0
fmt.Println(Sqrt(x), "Sqrt(", x, ")")
fmt.Println(math.Sqrt(x), "math.Sqrt(", x, ")")
}
这取决于具体情况。数值变化达到多少时循环应该继续?你认为多大的变化量才不算非常小?
在Go语言中判断两个浮点数是否发生微小变化,通常使用相对误差或绝对误差的方法。对于浮点数比较,由于精度问题,直接使用==运算符通常不可靠。
以下是几种常用的方法:
方法1:使用绝对误差
package main
import (
"fmt"
"math"
)
// 使用绝对误差判断
func nearlyEqual(a, b, epsilon float64) bool {
return math.Abs(a-b) <= epsilon
}
func main() {
v1 := 1.7320508075688772
v2 := 1.7320508075688774
// 设置一个很小的阈值
epsilon := 1e-14
if nearlyEqual(v1, v2, epsilon) {
fmt.Printf("v1和v2差异极小 (差异: %e)\n", math.Abs(v1-v2))
} else {
fmt.Printf("v1和v2有明显差异 (差异: %e)\n", math.Abs(v1-v2))
}
}
方法2:使用相对误差(推荐)
package main
import (
"fmt"
"math"
)
// 使用相对误差判断,更适合浮点数比较
func nearlyEqualRelative(a, b, epsilon float64) bool {
diff := math.Abs(a - b)
// 处理接近零的情况
if a == b {
return true
}
// 使用相对误差
relativeError := diff / math.Max(math.Abs(a), math.Abs(b))
return relativeError <= epsilon
}
func main() {
v1 := 1.7320508075688772
v2 := 1.7320508075688774
// 设置相对误差阈值
epsilon := 1e-15
if nearlyEqualRelative(v1, v2, epsilon) {
fmt.Printf("v1和v2差异极小 (相对误差: %e)\n",
math.Abs(v1-v2)/math.Max(math.Abs(v1), math.Abs(v2)))
} else {
fmt.Printf("v1和v2有明显差异 (相对误差: %e)\n",
math.Abs(v1-v2)/math.Max(math.Abs(v1), math.Abs(v2)))
}
}
方法3:结合绝对和相对误差
package main
import (
"fmt"
"math"
)
// 结合绝对误差和相对误差
func nearlyEqualCombined(a, b, absEpsilon, relEpsilon float64) bool {
diff := math.Abs(a - b)
// 如果绝对误差很小,直接返回true
if diff <= absEpsilon {
return true
}
// 否则检查相对误差
return diff <= math.Max(math.Abs(a), math.Abs(b)) * relEpsilon
}
func main() {
v1 := 1.7320508075688772
v2 := 1.7320508075688774
absEpsilon := 1e-15
relEpsilon := 1e-14
if nearlyEqualCombined(v1, v2, absEpsilon, relEpsilon) {
fmt.Println("v1和v2差异极小")
} else {
fmt.Println("v1和v2有明显差异")
}
}
在牛顿法示例中的应用
对于《Go语言之旅》中的牛顿法示例,可以这样修改:
package main
import (
"fmt"
"math"
)
func Sqrt(x float64) float64 {
z := 1.0
prev := 0.0
// 设置一个很小的阈值
epsilon := 1e-15
for {
prev = z
z -= (z*z - x) / (2*z)
// 当变化极小时停止循环
if math.Abs(z-prev) <= epsilon {
break
}
}
return z
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(math.Sqrt(2))
}
在实际应用中,相对误差方法通常更可靠,因为它考虑了数值的大小。对于你的具体示例,v1和v2之间的差异约为2e-16,使用1e-15作为阈值是合适的。

