背景
一つGo言語の面接質問を紹介します
以下の出力結果はなんですか?
package main
import (
"fmt"
)
func main() {
a := uint(0)
b := uint(1)
fmt.Println(a - b)
}
面接質問だから、明らかに-1ではないですね
この質問の正確な答えは
uintの最大値が出力されます
PCは32bitの場合は、uint32の最大値になり、2**32-1 になります,
PCは64bitの場合は、uinit64の最大値になり、2**64-1になります
※ なお、以下の公式サイトによるとuint64の最大値は18446744073709551615になり、2**64-1まではいかないです。
uint64 is the set of all unsigned 64-bit integers. Range: 0 through 18446744073709551615.
解説
まずaとbを二進数に変換します。たとえPCは32bitです。
a := uint(1)
上に対してPCは32bitの場合は、aのバイナリデータは以下になります
00000000 00000000 00000000 00000001
計算機は引き算を足し算として計算されてあるため、bは-2として見直していきます。
b := -uint(2)
上に対してPCは32bitの場合は、bのバイナリデータは以下になります。一番左の1は負を表しています。
10000000 00000000 00000000 00000010
aは正数ですので、aの2の補数はaの二進数そのものです
0000000 00000000 00000000 00000001
bは負数ですので、bの2の補数は以下のように計算されます
10000000 00000000 00000000 00000010 <- b(-2)の二進数
↑先頭の1は負数を表しています。
11111111 11111111 11111111 11111101 <- b(-2)の1の補数
↑先頭の1をそのまま、他の位置の二進数を逆転します
11111111 11111111 11111111 11111110 <- b(-2)の2の補数
1の補数を1を足したら、2の補数になります
負数の足し算の場合は、計算機は2の補数で行うため、a+bは以下になります
0と-1の2の補数を足し算します
00000000 00000000 00000000 00000001 <- aの2の補数
11111111 11111111 11111111 11111110 <- bの2の補数
11111111 11111111 11111111 11111111 <- a+bの2の補数
そじて以下のデータをuintに渡して出力しようとしていました
11111111 11111111 11111111 11111111
以下の方は渡しの憶測です、間違うことになったら、指摘してください。
aとbはuint型です、そのためa-bの結果もuint型です。
uintは符号なしなので、補数から二進数に変換して、出力するではなく、そのまま2の補数を正数として出力しようとしました
そのためa+bの2の補数の足し算の結果をそのまま出力します。
結果は2**32 - 1です。
もしaとbはuintではなく、intの場合は、最後に以下のように2の補数の結果を2進数に変換してから出力することになります。そのため、aとbはintの場合、結果は-1になります。
11111111 11111111 11111111 11111111 <- a+bの2の補数
11111111 11111111 11111111 11111110 <- a+bの1の補数
↑2の補数を1を引き算します
10000000 00000000 00000000 00000001 <- a+bの二進数
↑先頭の1をそのままで、他の位置の二進数を逆転します
まとめ###
引き算はPC側では負数の足し算として行っています
負数の足し算は2の補数として行っています
uintは符号なしですので、2の補数の結果はそのまま正数として出力します(要検討)