常量1截断为整数?

时间:2014-08-30 16:07:26

标签: types go weak-typing

为什么不编译这段代码?

package main
const a = 1.000001
const base = 0
const b = a+base
func main() {
    f(b)
}
func f(int) {}

$ go run a.go
# command-line-arguments
./a.go:4: constant 1 truncated to integer

它说1被截断了吗?或者1不能被截断?它在谈论哪一个?

有人回答上面的代码没有编译,因为bfloat64。但是为什么这会编译:

package main
import "fmt"
const a = 1.000001
const b = a-0.000001
func main() {
    fmt.Printf("%T %v\n",a,a)
    fmt.Printf("%T %v\n",b,b)
    f(b)
}
func f(int) {}

$ go run a.go 
float64 1.000001
float64 1

b此处为float64,但可以传递给f

4 个答案:

答案 0 :(得分:7)

go团队最近提出blog post about this,我建议你阅读。

从介绍中

  

Go是一种静态类型语言,不允许进行操作   混合数字类型。你不能将float64添加到int,甚至int32   到一个int。写1e6 * time.Second或math.Exp(1)或者是合法的   甚至1<<('\ t'+ 2.0)。在Go中,常量与变量不同,表现得很漂亮   很像普通的数字。这篇文章解释了为什么会这样,它是什么   装置

TLDR - 常量在Go中是无类型的。它们的类型只是在最后一刻才结晶。

这解释了你上面的问题。给定

func f(int) {}

然后

f(1) // ok
f(1.000) // OK
f(1.0E6) // OK
f(1.0001) // BAD

答案 1 :(得分:4)

对于常数,我有very strict conversion rules

  

在任何这些情况下,常数值x都可以转换为类型T:

     
      
  • x可以使用T类型的值表示。
  •   
  • x是一个浮点常量,T是一个浮点类型,使用IEEE 754进行舍入后,x可以用T类型的值表示   圆形到均匀的规则。常量T(x)是舍入值。
  •   
  • x是一个整数常量,T是一个字符串类型。在这种情况下,适用与非常数x相同的规则。
  •   

golang blog post about constants可能有助于进一步理解这一点。 由于严格性,违反引用规则的每次转换都被视为错误。这背后的原因是Go试图尽可能准确地表示常量。这也意味着最终类型是在使用的表达式的上下文中决定的。抛弃精度会使这失败并且是可能的编程错误的标志。

如果您真的想将值舍入为整数,请将其转换为变量(Example on play):

const b = 1.01
c := b
f(int(c))

这是有效的,因为编译器不跟踪值的来源和不适用于变量的常量规则。

但是为什么当我改变它时它会起作用? const a = 1.000001;const b = a-0.000001

在此示例中,b等于1. 1可以表示为整数,因此不涉及舍入和信息丢失。因此,这不是错误,因为它符合浮点值的转换规则(如前所述)。

答案 2 :(得分:2)

您的第一个程序可以像这样重写:

package main
func main() {
    f(1.000001)
}
func f(int) {}

显然没有将整数值传递给整数函数。

您的第二个程序可以像这样重写:

package main
import "fmt"
func main() {
    fmt.Printf("%T %v\n",1.000001,1.000001)
    fmt.Printf("%T %v\n",1,1)
    f(1)
}
func f(int) {}

看起来很好。

我所做的只是手动替换ab常量。这一切都可以。

答案 3 :(得分:1)

免责声明:我对Go没有任何经验,但以下答案基于与数据类型相关的一般原则。

您的函数f采用int类型的输入参数,但传递给它的实际值即b具有基于您的代码的浮点值。这将导致浮点值截断为整数值,如错误消息所示。

我相信您可以通过更改函数签名来修复此问题,以将浮点类型值作为输入参数,即

func f(float64) {}

Demo in Go

要将此与我熟悉的语言(C#)进行比较,您可以查看以下代码:

public static void Main(string[] args)
    {
        var a = 1.3;
        var b = 1.3 + 9;
        f(b);
        Console.WriteLine("Hello, world!");
    }

public static void f(int a)
    {
    }

使用var关键字,我们不会明确地生成数据类型a的{​​{1}}和b个变量。但是,由于正在为它们分配浮点值,因此推断它们的类型为double。现在,如果您将方法double定义为采用数据类型f的输入参数,然后传入inta。它会给你一个错误。但是,如果您将方法更改为b而不是double,则代码将编译而不会出现问题。

Demo in C#