这是 go 中的示例代码:
package main
import "fmt"
func mult32(a, b float32) float32 { return a*b }
func mult64(a, b float64) float64 { return a*b }
func main() {
fmt.Println(3*4.3) // A1, 12.9
fmt.Println(mult32(3, 4.3)) // B1, 12.900001
fmt.Println(mult64(3, 4.3)) // C1, 12.899999999999999
fmt.Println(12.9 - 3*4.3) // A2, 1.8033161362862765e-130
fmt.Println(12.9 - mult32(3, 4.3)) // B2, -9.536743e-07
fmt.Println(12.9 - mult64(3, 4.3)) // C2, 1.7763568394002505e-15
fmt.Println(12.9 - 3*4.3) // A4, 1.8033161362862765e-130
fmt.Println(float32(12.9) - float32(3)*float32(4.3)) // B4, -9.536743e-07
fmt.Println(float64(12.9) - float64(3)*float64(4.3)) // C4, 1.7763568394002505e-15
}
A1、B1 和 C1 行之间的结果差异是可以理解的。然而,从A2开始到C2的魔法来了。 B2 和 C2 的结果均与 A2 行的结果不匹配。对于 x2 行(x = A、B 或 C)也是如此 - 但 x2 和 x4 的输出是相同的。
为了确定,让我们以二进制形式打印结果。
fmt.Printf("%b\n", 3*4.3) // A11, 7262054399134925p-49
fmt.Printf("%b\n", mult32(3, 4.3)) // B11, 13526631p-20
fmt.Printf("%b\n", mult64(3, 4.3)) // C11, 7262054399134924p-49
fmt.Printf("%b\n", 12.9 - 3*4.3) // A12, 4503599627370496p-483
fmt.Printf("%b\n", 12.9 - mult32(3, 4.3)) // B12, -8388608p-43
fmt.Printf("%b\n", 12.9 - mult64(3, 4.3)) // C12, 4503599627370496p-101
fmt.Printf("%b\n", 12.9 - 3*4.3) // A14, 4503599627370496p-483
fmt.Printf("%b\n", float32(12.9) - float32(3)*float32(4.3)) // B14, -8388608p-43
fmt.Printf("%b\n", float64(12.9) - float64(3)*float64(4.3)) // C14, 4503599627370496p-101
上面代码中的一些事实(bin 形式中的一个):
问题来了:
代码已在 64 位 Linux 上的“go run”和“go build”(go1.0.3) 下进行了测试,也在该网站上进行了测试:http://tour.golang.org/
常数:
- 数字常量表示任意精度的值并且不会溢出。
- 表示至少 256 位的整数常量。
- 表示浮点常量,包括复数常量的部分,尾数至少为 256 位,带符号指数至少为 32 位。
是的,由编译器用于编译时常量。
是的,它们是不同的:涉及更高的精度。参见 1。
是的,参见1。
最大限度地减少多项浮点常量表达式的浮点误差累积。
当然可以。实现较低精度可以成为一个目标吗?运行时浮点运算本质上是不完美的,这已经足够了,不需要从常量表达式中添加更多的不精确性。
表示浮点常量,包括复数常量的部分,尾数至少为 256 位,带符号指数至少为 32 位。
请注意,Go 1.8(目前在 2016 年第四季度处于测试阶段,于 2017 年第一季度发布)更改了该定义:
语言规范现在仅要求实现支持浮点常量中的最多 16 位指数。
这不会影响“”或gc
编译器,它们仍然支持 32 位指数。gccgo
来自变更17711
:要求常数中的最小指数为 16 位,而不是 32spec
16 位二进制指数允许常数范围大致涵盖从 7e-9865 到 7e9863 的范围,这对于任何实际和假设的常数算术来说都绰绰有余。
此外,直到最近
都无法正确处理非常大的指数;也就是说,任何实际程序(除了探索极端情况的测试)受到影响的可能性都接近于零。cmd/compile
最后,限制最小支持范围显着降低了这一领域的实现复杂性,而对于新的或替代的符合规范的实现来说,实际上这几乎不重要,这些实现不或不能依赖于支持 32 位指数范围的预先存在的任意精度算术包.
这在技术上是一种语言更改,但由于上述原因,这不太可能影响任何实际程序,当然也不会影响使用 gc 或 gccgo 编译器编译的程序,因为它们目前支持高达 32 位指数。
参见 issue 13572 提到:
在 Go 1.4 中,编译器拒绝了大于 10000 的指数(因为知道代码不适用于更大的指数),而用户没有任何抱怨。
在 Go 的早期版本中,大指数被默默地错误处理,同样没有受到用户的任何抱怨。