go - 为什么用两种方式转换相同的值,结果却不同?
问题描述
相同常量的两种转换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
解决方案
如果您真正的问题是为什么一个尝试转换为int
产生编译时错误消息,而另一个产生一个非常负的整数,那是因为一个是编译时转换,另一个是运行时转换。我认为在这些情况下,明确您的期望是有帮助的,什么可以运行,什么不能运行。这是您的代码的Go Playground版本,其中最后一次转换已被注释掉。注释掉它的原因当然是它不能编译。
正如阿德里安在评论中指出的那样,Big
是一个常数,特别是一个无类型的常数。正如Uvelichitel 回答x
的那样,T
当且仅当
x
可由 type 的值表示T
。
(引用部分来自 Uvelichitel 链接部分,除了我为“可代表”这个词添加了内部链接。)
该表达式float64(Big)
是一个显式类型转换,其 是一个常量x
,因此结果是具有给定值的 float64 类型常量。到目前为止,这很好:现在我们有92233720368547758074444444
一个 float64。这切断了一些数字:实际的内部表示是92233720368547758080000000
(参见带有指令的变体%f
)。低位数字...74444444
已四舍五入为...80000000
。有关舍入的原因,请参阅“可表示”的链接。
该表达式int(float64(Big))
是围绕内部显式类型转换的外部显式类型转换。我们已经知道内部类型转换的作用:它产生float64
常量92233720368547758080000000.0
。外部转换试图将这个新值表示为int
,但它不适合,从而产生错误:
./prog.go:18:17: constant 92233720368547758080000000 overflows int
如果注释掉的行未注释。再次注意,由于内部转换,该值已被四舍五入。
另一方面,needFloat(Big)
是函数调用。调用该函数将无类型常量分配给它的参数 (a float64
) 并获得它的返回值(相同的float64
, value 92233720368547758080000000.0
。在给定默认或显式格式化指令的情况下打印您期望的内容。返回的值不是常量。
同样,int(needFloat(Big))
调用needFloat
,它返回与float64
以前相同的值,而不是常量。显式int
类型转换尝试将此值转换为int
在运行时,而不是在编译时。对于数字类型之间的这种转换,在https://golang.org/ref/spec#Conversions上有一个包含三个显式规则的列表,以及最后一个警告。这里适用规则 2:丢弃任何小数部分。但警告也适用:
在涉及浮点或复数值的所有非常量转换中,如果结果类型不能表示该值,则转换成功,但结果值取决于实现。
换句话说,没有运行时错误,但int
你得到的值——在这种情况下是-2147483648
,它是允许的最小 32 位整数——取决于实现。这个特定的实现选择使用这个特定的负数作为其结果。另一种实现可能会选择其他一些数字。(有趣的是,在操场上,如果我直接转换为,uint
我得到零。如果我转换为int
,然后转换为uint
,我得到我期望的 0x80000000。)
因此,是否出现错误的关键区别在于您是在编译时通过常量还是在运行时通过运行时转换进行转换。
推荐阅读
- for-loop - 如何使用循环和不存在的字段在 qlikview 中加载数据?
- r - 重塑 tidyr 数据
- spring - 我在我的 bean xml 中从 spring 2.5 升级到 4 时收到“javax.naming.NameNotFoundException”
- sql - 返回具有最高级别节点(根)的最低级别节点(叶子)
- odoo - 在odoo12的表单视图中隐藏操作/更多按钮
- pandas - 使用多索引列从 Pandas DataFrame 创建箱线图
- python - Mongoengine 查找没有字段的文档
- python - Django - 如何用多个对象保存表单?
- delphi - 在调整表单大小时,我们如何在 webbrowser 中保持第一行显示不变?
- angular - 所有 Angular 项目都存在与 component.ts 相同的问题