How does 1 << 64 – 1 work?

huangapple go评论106阅读模式
英文:

How does 1 << 64 - 1 work?

问题

在http://tour.golang.org/#14上,他们展示了一个例子,其中数字1被左移了64位。当然,这会导致溢出,但然后又减去了1,一切都正常。为什么表达式的一部分会导致失败,而整个表达式却正常工作呢?

思考:
我认为,将无符号数设置为比其允许的数字更大的数字是导致溢出的原因。似乎在表达式的右侧,内存分配比左侧更宽松?这是真的吗?

英文:

At http://tour.golang.org/#14 they show an example where the number 1 is shifted by 64 bits. This of course would result in an overflow, but then it is subtracted by 1 and all is well. How does half of the expression result in a failure while the entire expression as whole work just fine?

Thoughts:
I would assume that the setting of the unsigned to a number larger than what it allows is what causes the explosion. It would seem that memory is allocated more loosely on the right hand side of the expression than on the left? Is this true?

答案1

得分: 19

你的表达式的结果是一个(编译时)常量,因此在编译期间进行评估。语言规范要求:

> 常量表达式始终被准确评估;中间值和常量本身可能需要比语言中任何预声明类型支持的精度要高得多。以下是合法的声明:

> const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (无类型整数常量)
> const Four int8 = Huge >> 98 // Four == 4 (int8 类型)

来源:https://golang.org/ref/spec#Constant_expressions

英文:

The result of your expression is a (compile time) constant and the expression is therefore evaluated during compilation. The language specification mandates that

> Constant expressions are always evaluated exactly; intermediate values
> and the constants themselves may require precision significantly
> larger than supported by any predeclared type in the language. The
> following are legal declarations:
>
> const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (untyped integer constant)
> const Four int8 = Huge >> 98 // Four == 4 (type int8)

https://golang.org/ref/spec#Constant_expressions

答案2

得分: 5

这是因为Go编译器将常量表达式处理为数值常量。与必须遵守范围、存储位和溢出等规律的数据类型不同,数值常量永远不会失去精度。

数值常量只有在将它们赋值给一个变量时(该变量具有已知类型、数值范围和将数字存储到位的定义方式)才会被推导为具有有限精度和范围的数据类型。您还可以通过将它们用作包含非数值常量类型的方程的一部分来强制将它们推导为普通数据类型。

感到困惑吗?我也是...

这里有一篇更详细的关于数据类型和常量处理的文章:http://www.goinggo.net/2014/04/introduction-to-numeric-constants-in-go.html?m=1

英文:

That is because the Go compiler handles constant expressions as numeric constants. Contrary to the data-types that have to obey the law of range, storage bits and side-effects like overflow, numeric constants never lose precision.

Numeric constants only get deduced down to a data-type with limited precision and range at the moment when you assign them to a variable (which has a known type and thus a numeric range and a defined way to store the number into bits). You can also force them to get deduced to a ordinary data-type by using them as part of a equation that contains non Numeric constant types.

Confused? So was I..

Here is a longer write-up on the data-types and how constants are handled: http://www.goinggo.net/2014/04/introduction-to-numeric-constants-in-go.html?m=1

答案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)
}
英文:

I decided to try it. For reasons that are subtle, executing the expression as a constant expression (1 &lt;&lt; 64 -1) or piece by piece at run time gives the same answer. This is because of 2 different mechanisms. A constant expression is fully evaluated with infinite precision before being assigned to the variable. The step by step execution explicitly allows overflows and underflows through addition, subtraction and shift operations, and thus the result is the same.

See https://golang.org/ref/spec#Integer_overflow for a description of how integers are supposed to overflow.

However, doing it in groups, ie 1&lt;&lt;64 and then -1 causes overflow errors!

You can make a variable overflow though arithmetic, but you can not assign an overflow to a variable.

Try it yourself. Paste the code below into http://try.golang.org/

This one works:

// You can edit this code!
// Click here and start typing.
package main

import &quot;fmt&quot;

func main() {
  var MaxInt uint64 = 1
  MaxInt = MaxInt &lt;&lt; 64
  MaxInt = MaxInt  - 1
  fmt.Println(&quot;%d&quot;,MaxInt)
}

This one doesn't work:

// You can edit this code!
// Click here and start typing.
package main

import &quot;fmt&quot;

func main() {
  var MaxInt uint64 = 1 &lt;&lt; 64
  MaxInt = MaxInt  - 1
  fmt.Println(&quot;%d&quot;,MaxInt)
}

答案4

得分: 0

实际上,1 << 64 - 1 并不总是表示将 1 左移 64 位再减去 1。在大多数编程语言中,至少在我所了解的语言中(如C++、Java等),- 运算符会在 << 运算符之前应用。因此,1 << 64 - 1 等同于 1 << 63

但是,Go 语言的行为不同:https://golang.org/ref/spec#Operator_precedence

- 运算符在 << 运算符之后应用。

64 位左移的结果取决于数据类型。它就像在右侧添加 64 个 0,同时截断任何超出数据类型左侧的位。在某些语言中,溢出可能是有效的,而在其他一些语言中则不是。

编译器也可能根据解释的方式而有所不同,当你的位移大于或等于实际数据类型的大小时。我知道 Java 编译器会将实际的位移大小减去数据类型的大小,直到它小于数据字节的大小。

听起来很复杂,这里有一个关于 long 数据类型和 64 位 大小的简单示例。

所以 i << 64 等同于 i << 0 等同于 i

或者 i << 65 等同于 i << 1

或者 i << 130 等同于 i << 66 等同于 i << 2

正如前面所说,这可能因不同的编译器/语言而有所不同。没有一个固定的答案,必须参考特定的编程语言。

对于学习,我建议选择一个比 Go 更常见的语言,比如 C 语言系列的某个语言。

英文:

Actually 1 &lt;&lt; 64 - 1 does not always result in a left shift of 64 and minus 1. The - operator is applied before the &lt;&lt; operator in most languages, at least in any I know (like C++, Java, ...). Therefore 1 &lt;&lt; 64 - 1 <=> 1 &lt;&lt; 63.

But Go behaves different: https://golang.org/ref/spec#Operator_precedence

The - operator comes after the &lt;&lt; operator.

The result of a 64 Bit left shift is based on the data type. It's just like adding 64 of 0 on the right, while cutting any Bit that extend the data type on the left side. In some languages an overflow may be valid, in some other not.

Compilers may also behave different based on the interpretion when your shift is greater or equal than the actual data type size. I know that the Java compiler will reduce the actual shift as often by size of the data type until it's smaller than the size of the data byte.

Sounds difficult, but here and easy example for a long data type with 64 Bit size.

so i &lt;&lt; 64 <=> i &lt;&lt; 0 <=> i

or i &lt;&lt; 65 <=> i &lt;&lt; 1

or i &lt;&lt; 130 <=> i &lt;&lt; 66 <=> i &lt;&lt; 2.

As said, this may differ with different compilers / languages. There is never a solid answer without refering to a certain language.

For learning I would suggest a more common language than Go, maybe like something from the C family.

答案5

得分: 0

var MaxInt uint64 = 1<<64 - 1

位移操作
在二进制中,1后面有64个0(10000...)。
相当于2的64次方。

这是64位无符号整数的最大值(仅限正数)。
所以,我们减去1以防止错误。
因此,这是我们可以表示的最大值无符号整数。

英文:

var MaxInt uint64 = 1&lt;&lt;64 - 1

BITSHIFTING
In binary, 1, with 64 0s after it (10000...).
Same as 2^64.

How does 1 << 64 – 1 work?

This maxes out 64-bit unsigned integer (positive numbers only).
So, we subtract 1 to prevent the error.
Therefore, this is the maximum value unsigned integer we can write.

答案6

得分: 0

你可以在这里查看 go constant ,可以看到 int 实际上是一个 bigInt,所以 1 << 63 不会溢出。但是 var a int64 = 1 << 63 会溢出,因为你给 int64 赋了一个大于它能表示的值。

英文:

You can see here go constant int actually is a bigInt, so 1 << 63 won't overflow. But var a int64 = 1 &lt;&lt; 63 will overflow, because you are a assign a value bigger than int64.

huangapple
  • 本文由 发表于 2014年11月10日 05:16:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/26833421.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定