GoLang中使用浮点数的for循环会导致错误。

huangapple go评论92阅读模式
英文:

GoLang for loop with floats creates error

问题

有人能解释一下吗?我在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)
}

那么一切都正常。

有人知道为什么会出现这种情况吗?

提前感谢,
Rob

英文:

Can someone explain the following. I have a function in go that accepts a couple of float64 and then uses this value to calculate a lot of other values. The function looks like

func (g *Geometry) CalcStresses(x, zmax, zmin float64)(Vertical)

the result is put into a struct like

type Vertical struct {
	X float64
	Stresses []Stress
}

Now the funny thing is this. If I call the function like this;

for i:=14.0; i&lt;15.0; i+=0.1{
    result := geo.CalcStresses(i, 10, -10)
}

then I get a lot of results where the Stress array is empty, antoher interesting detail is that x sometimes shows like a number with a LOT of decimals (like 14.3999999999999999998)

However, if I call the function like this;

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

then everything is fine.

Does anyone know why this happens?

Thanks in advance,
Rob

答案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)
}
英文:

Not all real numbers can be represented precisely in binary floating point format, therefore looping over floating point number is asking for trouble.

From Wikipedia on Floating point

>The fact that floating-point numbers cannot precisely represent all real numbers, and that floating-point operations cannot precisely represent true arithmetic operations, leads to many surprising situations. This is related to the finite precision with which computers generally represent numbers.
>
>For example, the non-representability of 0.1 and 0.01 (in binary) means that the result of attempting to square 0.1 is neither 0.01 nor the representable number closest to it.

This code

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

produces this

14
14.1
14.2
14.299999999999999
14.399999999999999
14.499999999999998
14.599999999999998
14.699999999999998
14.799999999999997
14.899999999999997
14.999999999999996

You may use math.big.Rat type to represent rational numbers accurately.

Example

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

for i := x; i.Cmp(y) &lt; 0; i = i.Add(i, z) {
    v, _ := i.Float64()
    fmt.Println(v)
}

huangapple
  • 本文由 发表于 2013年12月11日 19:16:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/20517647.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定