为什么不编译这段代码?
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不能被截断?它在谈论哪一个?
有人回答上面的代码没有编译,因为b
是float64
。但是为什么这会编译:
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
。
答案 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) {}
看起来很好。
我所做的只是手动替换a
和b
常量。这一切都可以。
答案 3 :(得分:1)
免责声明:我对Go没有任何经验,但以下答案基于与数据类型相关的一般原则。
您的函数f
采用int
类型的输入参数,但传递给它的实际值即b
具有基于您的代码的浮点值。这将导致浮点值截断为整数值,如错误消息所示。
我相信您可以通过更改函数签名来修复此问题,以将浮点类型值作为输入参数,即
func f(float64) {}
要将此与我熟悉的语言(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
的输入参数,然后传入int
或a
。它会给你一个错误。但是,如果您将方法更改为b
而不是double
,则代码将编译而不会出现问题。