带有浮点数的GoLang for循环会产生错误

Bre*_*aas 4 floating-point for-loop rounding go

有人可以解释以下内容.我有一个函数在go中接受一些float64,然后使用这个值来计算很多其他值.功能看起来像

func (g *Geometry) CalcStresses(x, zmax, zmin float64)(Vertical)
Run Code Online (Sandbox Code Playgroud)

将结果放入类似的结构中

type Vertical struct {
    X float64
    Stresses []Stress
}
Run Code Online (Sandbox Code Playgroud)

现在有趣的是这个.如果我这样调用这个函数;

for i:=14.0; i<15.0; i+=0.1{
    result := geo.CalcStresses(i, 10, -10)
}
Run Code Online (Sandbox Code Playgroud)

然后我得到了很多结果,其中Stress数组是空的,另外有趣的细节是x有时显示像一个有​​很多小数的数字(如14.3999999999999999998)

但是,如果我这样调用这个函数;

for i:=0; i<10; i++{
    x := 14.0 + float64(i) * 0.1
    result := geo.CalcStresses(x,10,-10)
}
Run Code Online (Sandbox Code Playgroud)

一切都很好.

有谁知道为什么会这样?

罗伯,提前谢谢

Grz*_*Żur 6

并非所有实数都可以用二进制浮点格式精确表示,因此循环浮点数会产生麻烦.

来自维基百科的浮点

事实上,浮点数不能精确地表示所有实数,并且浮点运算不能精确地表示真正的算术运算,这会导致许多令人惊讶的情况.这与计算机通常表示数字的有限精度有关.

例如,0.1和0.01的不可表示性(二进制)意味着尝试平方0.1的结果既不是0.01也不是最接近它的可表示数.

这段代码

for i := 14.0; i < 15.0; i += 0.1 {
    fmt.Println(i)
}
Run Code Online (Sandbox Code Playgroud)

产生这个

14
14.1
14.2
14.299999999999999
14.399999999999999
14.499999999999998
14.599999999999998
14.699999999999998
14.799999999999997
14.899999999999997
14.999999999999996
Run Code Online (Sandbox Code Playgroud)

您可以使用math.big.Rat类型准确表示有理数.

x := big.NewRat(14, 1)
y := big.NewRat(15, 1)
z := big.NewRat(1, 10)

for i := x; i.Cmp(y) < 0; i = i.Add(i, z) {
    v, _ := i.Float64()
    fmt.Println(v)
}
Run Code Online (Sandbox Code Playgroud)