Golang中如何判断数值是否发生微小变化

Golang中如何判断数值是否发生微小变化 在练习《Go语言之旅》中的这个示例时遇到问题:

“接下来,修改循环条件,使得当值停止变化(或仅发生极小变化)时循环终止。”

v1 := 1.7320508075688772
v2 := 1.7320508075688774

如何判断v1和v2之间的差异是否属于极小变化?

6 回复

看起来没问题。

更多关于Golang中如何判断数值是否发生微小变化的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好的,感谢你的问题 😊 让问题变得简单了:

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

在实际应用中,相对误差方法通常更可靠,因为它考虑了数值的大小。对于你的具体示例,v1v2之间的差异约为2e-16,使用1e-15作为阈值是合适的。

回到顶部