go中的浮点运算

时间:2013-08-05 11:13:13

标签: floating-point go ieee-754 floating-point-precision

以下是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形式中的一个):

  1. A11和C11行之间存在差异(最后一位数 - 就在指数之前)。
  2. A12和C12线几乎相同(除了exponen !!!),在A14和C14线之间可以观察到相同的情况。
  3. 问题来了:

    1. 如何计算裸(裸:))数字? (每个Axx线的计算)
    2. 它们是由编译器/其他人执行的吗?
    3. 如果是,那为什么他们不一样?优化
    4. 它们是否在某些与IEE-754不同的系统中计算?
    5. 如果是,为什么呢?
    6. 这种方法能否达到更准确的精确度?
    7. 代码已在“go run”和“go build”(go1.0.3)以及该网站上的64位Linux上进行了测试:http://tour.golang.org/

2 个答案:

答案 0 :(得分:3)

  1. Constants

      
        
    • 数字常量表示任意精度的值,不会溢出。
    •   
    • 表示至少256位的整数常量。
    •   
    • 表示浮点常数,包括复数常量的部分,尾数至少为256位,有符号指数至少为32位。
    •   
  2. 是的,由编译器编译时间常量。

  3. 是的,它们是不同的:涉及更高的精确度。见1.

  4. 是的,请参阅1.

  5. 最大限度地减少多项浮点常量表达式的浮点错误累积。

  6. 当然是的。可以实现更低精度的目标吗?运行时浮点运算本质上是不完美的,不需要从常量表达式中添加更多不精确度就足够了。

答案 1 :(得分:0)

  

表示浮点常数,包括复数常量的部分,尾数至少为256位,有符号指数至少为32位。

请注意Go 1.8(目前在2017年第四季度发布的测试版,2017年第一季度发布)改变了这一定义:

  

语言规范现在只要求实现在浮点常量中支持最多16位指数
  这不会影响“gc”或gccgo编译器,两者都支持32位指数。

来自change 17711

  

spec:在常量中需要16位最小指数而不是32

     

16位二进制指数允许的范围大致范围从7e-9865到7e9863,这对于任何实际和假设的常数算法来说都足够了。

     

此外,直到最近cmd/compile无法正确处理非常大的指数;也就是说,任何真正的程序(但是对于探索角落情况的测试)受到影响的几率接近于零。

     

最后,限制最小支持范围可显着降低实际复杂性的区域,这对于新的或替代的符合规范的实现而言并不重要,这些实现不依赖于或不依赖于支持32位的预先存在的仲裁精度算术包指数范围。

     

这在技术上是一种语言变化,但由于上述原因,这不太可能影响任何真正的程序,当然也不会影响使用gc或gccgo编译器编译的程序,因为它们目前支持最多32位指数。

请参阅issue 13572提及:

  

在Go 1.4中,编译器拒绝了大于10000的指数(由于知道代码不适用于较大的指数),没有任何用户投诉。

     

在Go的早期版本中,大型指数无声地被错误处理,再次没有任何用户投诉。