Go 为什么用两种方法转换相同的值,结果会不同?

Go 为什么用两种方法转换相同的值,结果会不同?,go,floating-point,type-conversion,int,Go,Floating Point,Type Conversion,Int,将相同常量转换为float64的两种方法返回相同的值,但当我尝试将这些新值转换为int时,结果不同 ... const Big = 92233720368547758074444444 func needFloat(x float64) float64 { return x } func main() { fmt.Println(needFloat(Big)) fmt.Println(float64(Big)) fmt.Println(int(needFl

将相同常量转换为
float64
的两种方法返回相同的值,但当我尝试将这些新值转换为
int
时,结果不同

...

const Big = 92233720368547758074444444

func needFloat(x float64) float64 {
    return x
}

func main() {
    fmt.Println(needFloat(Big))
    fmt.Println(float64(Big))

    fmt.Println(int(needFloat(Big)))
    fmt.Println(int(float64(Big)))
}
我希望前两个
Println
返回相同类型的值

fmt.Println(needFloat(Big)) // 9.223372036854776e+25
fmt.Println(float64(Big))   // 9.223372036854776e+25
因此,当我将它们转换为
int
时,我希望得到相同的输出,但是:

fmt.Println(int(needFloat(Big))) // -2147483648
fmt.Println(int(float64(Big)))   // constant 92233720368547758080000000 overflows int
如果x可由表示,则常量值x可转换为类型T T的值

非常量值x可以转换为以下任意类型的T 案例: -x的类型和T都是整数或浮点类型


如果您真正的问题是为什么一个尝试转换为
int
会产生编译时错误消息,而另一个会产生非常负的整数,那是因为一个是编译时转换,另一个是运行时转换。我认为,在这些情况下,明确你期望的是什么,什么可以运行,什么不能运行是有帮助的。这是您的代码的一个版本,最后的转换被注释掉了。注释它的原因当然是它没有编译

正如,
Big
是一个常量,特别是一个非类型的常量。同样,常数
x
(任何类型)可以转换为新的不同类型
T
当且仅当

x
由类型为
T
的值指定

(引用部分来自Uvelichitel linked一节,除了我的添加了单词“representable”的内部链接。)

表达式
float64(大)
是一种显式类型转换,其
x
为常量,因此结果是具有给定值的float64类型常量。到目前为止,这很好:现在我们有了
922337203685477580744444
作为浮动64。这就去掉了一些数字:实际的内部表示是
9223372036854775808000000
(请参阅)。低位(
…74444444
)已四舍五入为
…8000000
。有关舍入的原因,请参见“可表示”链接

表达式
int(float64(Big))
是围绕内部显式类型转换的外部显式类型转换。我们已经知道内部类型转换的作用:它生成
float64
常量
9223372036854775808000000.0
。外部转换尝试将此新值表示为
int
,但它不适合,从而产生错误:

./prog.go:18:17: constant 92233720368547758080000000 overflows int
如果注释掉的行未注释。再次注意,由于内部转换,该值已四舍五入

另一方面,
needFloat(大)
是一个函数调用。调用该函数会将非类型常量分配给其参数(a
float64
),并获取其返回值(相同的
float64
,value
9223372036854775808000000.0
。在给定默认或显式格式化指令的情况下,打印所需内容。返回值不是常量

类似地,
int(needFloat(大))
调用
needFloat
,它返回与以前相同的
float64
值,而不是一个常量。
int
显式类型转换尝试在运行时而不是在编译时将此值转换为
int
。对于数值类型之间的此类转换,在,以及最后一条警告。在这里,规则2适用:任何分数部分都将被丢弃。但警告也适用于:

在所有涉及浮点或复数值的非常量转换中,如果结果类型不能表示值,则转换成功,但结果值取决于实现

换句话说,没有运行时错误,但是您得到的
int
值(在本例中为
-2147483648
)是允许的最小32位整数,这取决于实现。此特定实现选择使用此特定负数作为其结果。另一个实现可能选择其他值er编号。(有趣的是,在操场上,如果我直接转换成
uint
我得到零。如果我转换成
int
,然后转换成
uint
,我得到我期望的0x8000000。)


因此,在是否得到错误方面的关键区别在于,是在编译时通过常量进行转换,还是在运行时通过运行时转换进行转换。

奇怪,如果执行
f:=float64(大);fmt.Println(int(f))
也很有效,因为
Big
是一个非类型常量,因此可以根据使用它的上下文将其解释为不同的类型,如规范中所述:
int(needFloat(Big)) //is non-constant expression because of function call
./prog.go:18:17: constant 92233720368547758080000000 overflows int