英文:
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.
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论