3

为什么这段代码不能编译?

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 不能被截断?它在说哪个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是 a float64here,但可以传递给f.

4

4 回答 4

7

Go 团队最近发表了一篇关于此的博客文章,我建议你阅读。

从介绍

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
于 2014-08-30T17:03:07.280 回答
4

Go has very strict conversion rules for constants:

A constant value x can be converted to type T in any of these cases:

  • x is representable by a value of type T.
  • x is a floating-point constant, T is a floating-point type, and x is representable by a value of type T after rounding using IEEE 754 round-to-even rules. The constant T(x) is the rounded value.
  • x is an integer constant and T is a string type. The same rule as for non-constant x applies in this case.

The golang blog post about constants may be helpful in understanding this further. Due to the strictness every conversion that violates the quoted rules is considered an error. The reasoning behind this is that Go attempts to represent constants as accurately as possible. This also means that the final type is decided in the context of the used expression. Throwing away the precision defeats this and is a sign for a possible programming error.

If you really want to round a value to an integer, convert it to a variable (Example on play):

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

This works since the compiler does not track the origin of the value and constant rules to not apply to variables.

But then why does it work when I change it to this? const a = 1.000001;const b = a-0.000001

In this example b equals 1. 1 can be represented as an integer, so no rounding and information loss is involved. Therefore this is not an error since it complies with the conversion rules for float values (as quoted before).

于 2014-08-30T17:07:34.410 回答
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) {}

看起来不错。

我所做的只是手动替换aandb常量。这就是一切。

于 2014-09-03T13:21:12.043 回答
1

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

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

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

func f(float64) {}

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关键字,我们不会显式地创建ab变量 datatype double。但是,因为浮点值被分配给它们,所以它们的类型被推断为double. 现在,如果您将该方法定义f为采用数据类型的输入参数,int然后传入aor b。它会给你一个错误。但是,如果您将方法更改为double取值而不是int,您的代码将毫无问题地编译。

C# 中的演示

于 2014-08-30T16:12:37.370 回答