在http://tour.golang.org/#14,它们显示了数字1移位64位的示例。这当然会导致溢出,但随后减去1并且一切都很好。如果整个表达式整体工作得很好,表达的一半会导致失败吗?
思想:
我认为将unsigned设置为大于允许数量的数字是导致爆炸的原因。看起来内存在表达式的右侧比在左侧更松散地分配?这是真的吗?
答案 0 :(得分:16)
表达式的结果是(编译时)常量,因此在编译期间计算表达式。
规定了语言规范始终精确计算常量表达式;中间值 并且常数本身可能需要精确度 大于语言中任何预先声明的类型所支持的值。该 以下是法律声明:
const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (untyped integer constant) const Four int8 = Huge >> 98 // Four == 4 (type int8)
答案 1 :(得分:4)
这是因为Go编译器将常量表达式作为数字常量处理。与必须遵守范围定律,存储位和溢出等副作用的数据类型相反,数值常量永远不会失去精度。
数值常量只会被推导到具有有限精度和范围的数据类型,此时将它们分配给变量(具有已知类型,因此具有数字范围和定义的方式将数字存储到位中) )。您还可以通过将它们用作包含非数字常量类型的等式的一部分来强制它们推断为普通数据类型。
困惑?我也是...... ..
以下是关于数据类型以及如何处理常量的更长时间的写法:http://www.goinggo.net/2014/04/introduction-to-numeric-constants-in-go.html?m=1
答案 2 :(得分:1)
实际上1 << 64 - 1
并不总是会导致64和-1的左移。-
运算符在大多数语言的<<
运算符之前应用,至少在我知道的任何语言中都是如此(比如C ++,Java,......)。因此1 << 64 - 1
&lt; =&gt; 1 << 63
。
但Go
表现不同:https://golang.org/ref/spec#Operator_precedence
-
运算符位于<<
运算符之后。
64位左移的结果基于数据类型。它就像在右边添加64的0,同时削减任何扩展左侧数据类型的Bit。在某些语言中,溢出可能是有效的,而另一些则不是。
当您的班次大于或等于实际数据类型大小时,编译器也可能基于解释而表现不同。我知道Java编译器会根据数据类型的大小来减少实际的移位,直到它小于数据字节的大小。
听起来很难,但这是一个简单的long
64 Bit
数据类型示例。
所以i << 64
&lt; =&gt; i << 0
&lt; =&gt; i
或i << 65
&lt; =&gt; i << 1
或i << 130
&lt; =&gt; i << 66
&lt; =&gt; i << 2
。
如上所述,这可能因编译器/语言不同而有所不同。没有引用某种语言就没有可靠的答案。
对于学习,我建议使用比Go
更常用的语言,或许像来自C
家庭的语言。
答案 3 :(得分:1)
我决定尝试一下。由于微妙的原因,在运行时将表达式作为常量表达式(1 << 64 -1
)或逐段执行会给出相同的答案。这是因为有两种不同的机制。在分配给变量之前,将以无限精度完全计算常量表达式。逐步执行明确允许通过加法,减法和移位操作进行溢出和下溢,因此结果是相同的。
有关整数应如何溢出的说明,请参阅https://golang.org/ref/spec#Integer_overflow。
但是,分组执行,即1<<64
然后-1
会导致溢出错误!
您可以通过算术使变量溢出,但不能将溢出分配给变量。
亲自尝试一下。将以下代码粘贴到http://try.golang.org/
中这个有效:
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
var MaxInt uint64 = 1
MaxInt = MaxInt << 64
MaxInt = MaxInt - 1
fmt.Println("%d",MaxInt)
}
这个不起作用:
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
var MaxInt uint64 = 1 << 64
MaxInt = MaxInt - 1
fmt.Println("%d",MaxInt)
}
答案 4 :(得分:0)