无符号整数的溢出

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

Overflow of unsigned integers

问题

根据Go语言规范中的说明,对于无符号整数溢出的情况:

  • 对于无符号整数值,操作+、-、*和<<会对2的n次方取模,其中n是无符号整数类型的位宽度。简单来说,这些无符号整数操作在溢出时会丢弃高位,程序可以依赖于"循环回绕"。

你尝试进行测试,但得到了不一致的结果。根据你提供的链接(http://play.golang.org/p/sJxtSHbigT),代码如下:

package main

import "fmt"

func main() {
    fmt.Println("test")
    var num uint32 = 1 << 35
}

这会导致错误:

prog.go:7: constant 34359738368 overflows uint32
 [process exited with non-zero status]

但根据规范,不应该出现错误,而是应该看到结果为0。

英文:

Go spec say on unsigned integer overflow:

> For unsigned integer values, the operations +, -, *, and << are
> computed modulo 2n, where n is the bit width of the unsigned integer's
> type. Loosely speaking, these unsigned integer operations discard high
> bits upon overflow, and programs may rely on ''wrap around''.

I try to test it, but get inconsistent result - http://play.golang.org/p/sJxtSHbigT:

package main

import &quot;fmt&quot;

func main() {
	fmt.Println(&quot;test&quot;)
	var num uint32 = 1 &lt;&lt; 35
}

This give error:

prog.go:7: constant 34359738368 overflows uint32
 [process exited with non-zero status]

But according to spec should be no error but rather I should seen 0.

答案1

得分: 9

你引用的规范特指“+、-、*和<<操作的结果”。你试图定义一个常量,而不是查看这些操作的结果。

你也不能使用这些超大的值作为这些操作的输入。编译器不会为你包装任何值;这只是这些操作的运行时行为。

package main

import "fmt"

func main() {
    var num uint32 = 1 + 1 << 35
    fmt.Printf("num = %v\n", num)
}
prog.go:6: constant 34359738369 overflows uint32
 [process exited with non-zero status]

下面是一个有趣的例子。

var num uint32 = (1 << 31) + (1 << 31)
fmt.Printf("num = %v\n", num)
prog.go:6: constant 4294967296 overflows uint32
 [process exited with non-zero status]

在这种情况下,编译器试图在编译时计算(1 << 31) + (1 << 31),得到常量值4294967296,它太大而无法容纳。

var num uint32 = (1 << 31)
num += (1 << 31)
fmt.Printf("num = %v\n", num)
num = 0

在这种情况下,加法是在运行时执行的,值会像你预期的那样进行包装。

英文:

The specification you quote refers specifically to the results of "the operations +, -, *, and <<". You're trying to define a constant, not looking at the result of one of those operations.

You also can't use those over-sized values for the input of those operations. The compiler won't wrap any values for you; that's just the runtime behaviour of those operations.

package main

import &quot;fmt&quot;

func main() {
	var num uint32 = 1 + 1 &lt;&lt; 35
    fmt.Printf(&quot;num = %v\n&quot;, num)
}

<!-- -->

prog.go:6: constant 34359738369 overflows uint32
 [process exited with non-zero status]

Here's an interesting example.

var num uint32 = (1 &lt;&lt; 31) + (1 &lt;&lt; 31)
fmt.Printf(&quot;num = %v\n&quot;, num)

<!-- -->

prog.go:6: constant 4294967296 overflows uint32
 [process exited with non-zero status]

In this case, the compiler attempts to evaluate (1 &lt;&lt; 31) + (1 &lt;&lt; 31) at compile-time, producing the constant value 4294967296, which is too large to fit.

var num uint32 = (1 &lt;&lt; 31)
num += (1 &lt;&lt; 31)
fmt.Printf(&quot;num = %v\n&quot;, num)

<!-- -->

num = 0

In this case, the addition is performed at run-time, and the value wraps around as you'd expect.

答案2

得分: 3

这是因为1 << 35是一个无类型的常量表达式(它只涉及数值常量)。直到你赋值给它之前,它才成为uint32类型。Go语言禁止你将一个常量表达式赋值给一个变量,如果这样做会导致溢出,因为这种情况几乎肯定是无意的。

英文:

That's because 1 &lt;&lt; 35 is an untyped constant expression (it only involves numerical constants). It doesn't become an uint32 until you assign it. Go prohibits you to assign to a variable a constant expression that would overflow it as stuff like that is almost certainly unintentional.

huangapple
  • 本文由 发表于 2014年3月17日 06:32:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/22443972.html
匿名

发表评论

匿名网友

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

确定