进行具有不同结果的数学运算

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

Go math operations with different results

问题

这段代码涉及浮点数精度的问题。你的测试函数包含两个表达式,我认为它们应该产生相等的结果,但是给定的输入却产生了不同的输出。

第一个表达式(value/plays) + rate的输出结果是:

0.2
0.30000000000000004
0.4
0.5
0.6

而第二个表达式(plays*rate + value) / plays的输出结果是预期的:

0.2
0.3
0.4
0.5
0.6

为什么会出现这种情况呢?

英文:

(I hope) this is not a question about the accuracy of floating point math in Go. Below I have a test function with 2 expressions that I think should produce equal results, but given the inputs, produce different output.

Edit This ended up being just about floating point precision.

Playground Source

package main

import "fmt"

func main() {
	fmt.Println(test(10.0, 0.1, 1.0)) // 0.2
	fmt.Println(test(10.0, 0.2, 1.0)) // 0.3
	fmt.Println(test(10.0, 0.3, 1.0)) // 0.4
	fmt.Println(test(10.0, 0.4, 1.0)) // 0.5
	fmt.Println(test(10.0, 0.5, 1.0)) // 0.6
}

func test(plays float64, rate float64, value float64) float64 {
	// return (value/plays) + rate
	return (plays*rate + value) / plays
}

The first expression (value/plays) + rate prints:

0.2
0.30000000000000004
0.4
0.5
0.6

while the second expression (plays*rate + value) / plays prints the expected output:

0.2
0.3
0.4
0.5
0.6

Why is this?

答案1

得分: 1

由于涉及浮点数运算,即使输入相同,两个表达式的结果也不会相同。一旦舍入到较小的精度位置,它们可能看起来相同。

其中一个输出显示小数点后17位数字,而其余的只显示1位,可能是因为在格式化输出时,Println对浮点数值进行舍入导致了边界条件的问题。为了演示边界条件,考虑以下代码:

package main

import "fmt"

func main() {
    fmt.Println(0.30000000000000000)
    fmt.Println(0.30000000000000001)
    fmt.Println(0.30000000000000002)
    fmt.Println(0.30000000000000003)
    fmt.Println(0.30000000000000004)
    fmt.Println(0.30000000000000005)
    fmt.Println(0.30000000000000006)
    fmt.Println(0.30000000000000007)
    fmt.Println(0.30000000000000008)
}

输出结果为:

0.3
0.3
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.3000000000000001

前两个值的结果被截断,因此输出显示精度为1,而其他值被四舍五入,但精度要高得多。我没有查看fmt包的源代码,但这似乎是Println截断浮点数值的方式导致的结果。

将上述问题中的playground代码更改为使用Printf,并设置较大的精度值,可以了解实际结果的样子。以下是修改后的代码在Go Playground中的展示。

英文:

Since floating point arithmetic is involved, the results for the two expressions will not be the same, even when the input is the same. They may appear to be the same once rounded off to a smaller number of precision positions.

The reason one of the outputs shows 17 digits after the decimal, while the remaining show only 1, is probably because you are hitting a boundary condition with the 3.0000.... result for the way Println rounds off the floating point values while formating output. To demonstrate the boundary condition, consider the following code:

package main

import "fmt"

func main() {
	fmt.Println(0.30000000000000000)
	fmt.Println(0.30000000000000001)
	fmt.Println(0.30000000000000002)
	fmt.Println(0.30000000000000003)
	fmt.Println(0.30000000000000004)
	fmt.Println(0.30000000000000005)
	fmt.Println(0.30000000000000006)
	fmt.Println(0.30000000000000007)
	fmt.Println(0.30000000000000008)
}

The output is:

0.3
0.3
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.30000000000000004
0.3000000000000001

The results of the first two values are truncated and hence the output shows precision of 1 while the other values are rounded off but the precision is much greater. I did not look into the source code of fmt package but this seems to be a result of the way Println truncates floating point values.

Changing the playground code in the question above to use Printf with a large value for precision gives an idea about what the results actually look like. Here's the modified code in the Go Playground

huangapple
  • 本文由 发表于 2014年11月19日 10:50:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/27008113.html
匿名

发表评论

匿名网友

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

确定