使用浮点数的GoLang循环会创建错误

4
有人能解释一下以下内容吗?我有一个在Go中的函数,接受几个float64值,然后使用这些值计算出许多其他值。该函数如下:
func (g *Geometry) CalcStresses(x, zmax, zmin float64)(Vertical)

结果被放进一个类似于结构体的数据结构中。
type Vertical struct {
    X float64
    Stresses []Stress
}

现在有趣的事情是,如果我像这样调用函数;
for i:=14.0; i<15.0; i+=0.1{
    result := geo.CalcStresses(i, 10, -10)
}

我搜索到很多的结果显示Stress数组是空的,另一个有趣的细节是有时候x的值显示成带有很多小数点的数字(例如14.3999999999999999998)。

然而,如果我这样调用函数;

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

那么一切都很好。

有人知道为什么会发生这种情况吗?

提前感谢, 罗布


1
你可能会发现这些资源有帮助:http://www.validlab.com/goldberg/paper.pdf 和 http://floating-point-gui.de/ - Volker
哇,谢谢Volker,这绝对是我能得到的最全面的答案 :-) 很棒的论文,谢谢! - BreinBaas
1个回答

6

并非所有实数都可以在二进制浮点格式中精确表示,因此循环浮点数可能会引起问题。

来自浮点数的维基百科

浮点数无法精确表示所有实数,浮点运算也无法精确表示真正的算术运算,这导致了许多令人惊讶的情况。这与计算机通常表示数字的有限精度有关。

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

此代码

for i := 14.0; i < 15.0; i += 0.1 {
    fmt.Println(i)
}

生成这个

14
14.1
14.2
14.299999999999999
14.399999999999999
14.499999999999998
14.599999999999998
14.699999999999998
14.799999999999997
14.899999999999997
14.999999999999996

你可以使用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)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接