技術

【Golang】package strconvのintSize = 32 << (∧uint(0) >> 63)は何をしているのか

投稿日:

こんにちは

Goのstrconvのパッケージを読んでたらintSizeというものがありました。

const intSize = 32 << (^uint(0) >> 63)

intSizeはAtoiメソッド内で使用されています。

if intSize == 32 && (0 < sLen && sLen < 10) || intSize == 64 && (0 < sLen && sLen < 19)

見た感じintSizeには32か64が代入されるであろうということは分かるのですが、なぜその式になるのかが分からなったので調べてみました。

<<>>はビット演算子です。<<が左シフト演算で>>が右シフト演算です。
^はビット演算(XOR)です。

使い方はこんな感じ。

package main

import "fmt"

const (
	A uint8 = 44
)

func main() {
	a := fmt.Sprintf("2進数: %b, 10進数: %d", A, A)
	fmt.Println(a)
	// 2進数: 00101100, 10進数: 44

	l := A << 2
	b := fmt.Sprintf("2進数: %b, 10進数: %d", l, l)
	fmt.Println(b)
	// 2進数: 10110000, 10進数: 176

	r := A >> 2
	c := fmt.Sprintf("2進数: %b, 10進数: %d", r, r)
	fmt.Println(c)
	// 2進数: 1011, 10進数: 11

	d := fmt.Sprintf("2進数: %b, 10進数: %d", ^A, ^A)
	fmt.Println(d)
	// 2進数: 11010011, 10進数: 211
}

^は整数演算子としても用意されていて反転の意味になります。44の2進数、00101100を反転すると11010011になります。

公式ではこのように書かれています。

^x bitwise complement is m ^ x with m = “all bits set to 1” for unsigned x and m = -1 for signed x

mには毎回1がセットされ、1ビット毎に排他的論理和演算を行っています。

排他的論理和演算は

0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0

という性質を持ちます。

先程の^Aでいうと

1 ^ 0 = 1
1 ^ 0 = 1
1 ^ 1 = 0
1 ^ 0 = 1
1 ^ 1 = 0
1 ^ 1 = 0
1 ^ 0 = 1
1 ^ 0 = 1

を内部で行っており、結果11010011となります。

ここまでは分かれば式で何が行われているのかが分かります。
それでは元のソースコードに戻ります。

const intSize = 32 << (^uint(0) >> 63)

最初に実行されるのは

(^uint(0) >> 63)

の部分でその後に

32 << 上記の計算結果

が実行されます。

uintは環境依存で、32bitか64bitになります。
下記の2つがそれぞれの計算結果になります。

uintが32bitの場合

uint(0) 32bit0埋めにする
0000 0000 0000 0000 0000 0000 0000 0000

^uint(0) 反転する
1111 1111 1111 1111 1111 1111 1111 1111

^uint(0) >> 63 右シフト演算で63bitシフトさせる。結果0になる
0000 0000 0000 0000 0000 0000 0000 0000

左シフト演算
32 << 0 = 32
intSize = 32

uintが64bitの場合

uint(0) 64bit0埋めにする
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

^uint(0) 反転する
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

^uint(0) >> 63 右シフト演算で63bitシフトさせる。結果1になる
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001

左シフト演算
32 << 1 = 64
intSize = 64

結果intSizeには32か64が代入されます。
スマート!!

最初見た時はよく分からなかったのですが、調べてみたらとても面白かったです。
引き続きGoのソースリーディングはしていくので、また面白いものがあったらブログに書いていこうと思います。

参考URL
The Go Programming Language Specification

-技術

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

no image

【ionic】タブをタップで最上部にスクロール!

どうも、中田です。 今日はタブをタップでトップにゴーの日です。 どういう日かとい …

no image

【Grunt】楽々デプロイ(grunt-rsync)

どうも、中田です。 gruntでアプリをデプロイするときにどうやったらいいかぁ〜 …

Slack Tasker系アプリで絵文字ステータスを自動設定(android 「Automate」)

Slackの絵文字ステータス、皆さん使っているでしょうか? 私は自分の居る場所に …

no image

[Rails]link_toをhelperから使う

こんばんは Railsでviewを作成している時に、helperからlink_t …

no image

【Angular7】ファイルの指定をルート相対パスにする

こんにちは Angularで別ファイル(例えばcomponentからservic …