Go 语言中的整数回绕现象

整数类型虽然不会像浮点类型那样因为舍入错误而导致不精确, 但整数类型也有它们自己的问题, 那就是有限的取值范围。 在 Go 语言中, 当超过整数类型的取值范围时, 就会出现整数回绕(wrap around)现象。

例如, 8 位无符号整数 uint8 类型的取值范围为 0 ~ 255 ,而针对该类型的增量操作在结果超过 255 时将回绕至 0 。 作为例子, 代码清单 7-2 就通过执行增量操作触发了有符号和无符号 8 位整数的回绕现象。


代码清单 7-2 整数回绕 integers-wrap.go

var red uint8 = 255
red++
fmt.Println(red)    // 打印出“0”

var number int8 = 127
number++
fmt.Println(number)    // 打印出“-128”

聚焦二进制位

为了了解整数出现回绕的原因, 我们需要将注意力放到二进制位上, 为此需要用到格式化变量 %b , 它可以以二进制位的形式打印出相应的整数值。 跟其他格式化变量一样, %b 也可以启用零填充功能并指定格式化输出的最小长度, 就像代码清单 7-3 所示的那样。


代码清单 7-3 打印二进制位: bits.go

var green uint8 = 3
fmt.Printf("%08b\n", green)    // 打印出”00000011“
green++
fmt.Printf("%08b\n", green)    // 打印出“00000100”

在代码清单 7-3 中, 对 green 的值执行加 1 操作将导致 1 进位, 而 0 则被留在原位, 最终计算得出二进制数 00000100 , 也就是十进制数 4 , 这个过程如图 7-1 所示。


图 7-1 在二进制加法中对 1 实施进位

../_images/7-1.png

正如代码清单 7-4 以及图 7-2 所示, 在对值为 2558 位无符号整数 blue 执行增量运算的时候, 同样的进位操作将再次出现, 但这次进位跟前一次进位有一个重要的区别: 对只有 8 位的变量 blue 来说, 最高位进位的 1 将“无处容身”, 并导致变量的值变为 0


代码清单 7-4 二进制位在整数回绕时的状态: bits-wrap.go

var blue uint8 = 255
fmt.Printf("%08b\n", blue)    // 打印出”11111111“
blue++
fmt.Printf("%08b\n", blue)    // 打印出”00000000“

图 7-2 ”无处容身“的进位

../_images/7-2.png

虽然回绕在某些情况下可能正好是你想要获得的状态, 但是有时候也会成为问题。 最简单的避免回绕的方法就是选用一种足够长的整数类型, 使它能够容纳你想要存储的值。

Note

本文摘录自《Go语言趣学指南》第 7 章, 请访问 gpwgcn.com 以获取更多相关信息。

../_images/gpwgcn1.jpg