Skip to content

Instantly share code, notes, and snippets.

@Code-Hex
Created February 9, 2022 15:42
Show Gist options
  • Save Code-Hex/b113083b9631f63de9b9ddc72e8c703e to your computer and use it in GitHub Desktop.
Save Code-Hex/b113083b9631f63de9b9ddc72e8c703e to your computer and use it in GitHub Desktop.
@sters と一緒に学んだ符号拡張をコードかする方法
package bit
import (
"math"
"testing"
)
// SignExtend は a の値を bitSize で指定される値を解釈し、変換したものを返します。
// a の値は 32bit 以下の任意のbitSizeの値が入ってくる可能性があります。
// unsignedが入ってくる可能性は考慮されていません。(仕様外)
func SignExtend(a uint32, bitSize uint32) int32 {
// TODO: オーバーフロー時にエラーを返す?
// https://go.dev/play/p/YRkiziciA2g
// 指定されたビットにおける負数か判定し、負数の場合は32ビットに対して足りないビットを埋める
// 1<<(bitSize-1) の部分、bitSize = 8 の場合
// 0b00000000_00000000_00000000_00000001 << (8 - 1)
// 0b00000000_00000000_00000000_10000000
//
// 1<<(bitSize-1) <= a はこのようになる
// 0b00000000_00000000_00000000_10000000 < a
//
// aがbitSize=8における-10のとき
// 0b11110110
//
// uint32で入ってくるのでこうなる
// 0b00000000_00000000_00000000_11110110
//
// 1<<(bitSize-1) <= a は最終的にこうなる
// 0b00000000_00000000_00000000_10000000 < 0b00000000_00000000_00000000_11110110
//
// なので a は bitSize=8 における負数である、として負数用の処理をする
if 1<<(bitSize-1) <= a {
// やりたいことは足りないビットを埋めること
// もとの値を 0b00000000_00000000_00000000_11110110
// こうしたい 0b11111111_11111111_11111111_11110110
// そのため・・・
// こういうマスクをつくって 0b11111111_11111111_11111111_11111111
// bitSize分シフト(8の場合) 0b11111111_11111111_11111111_00000000
// もとの値と | すると 0b11111111_11111111_11111111_11110110
// -> 足りていないビットが埋まった
return int32(a | (math.MaxUint32 << bitSize))
}
return int32(a)
}
// --- FAIL: TestSignExtend (0.00s)
// --- FAIL: TestSignExtend/#00 (0.00s)
// /Users/codehex/go/src/github.com/Code-Hex/sign/bit_test.go:88: (bit.args{a:0xf6, bitSize:0x8}) want -10 but got 246
// --- FAIL: TestSignExtend/#02 (0.00s)
// /Users/codehex/go/src/github.com/Code-Hex/sign/bit_test.go:88: (bit.args{a:0xe38, bitSize:0xc}) want -456 but got 3640
// https://www.rapidtables.com/convert/number/binary-to-hex.html
func TestSignExtend(t *testing.T) {
type args struct {
a uint32
bitSize uint32
}
cases := []struct {
name string
args args
want int32
}{
{
// https://ja.wikipedia.org/wiki/%E7%AC%A6%E5%8F%B7%E6%8B%A1%E5%BC%B5
args: args{
a: 0b11110110, // 8bit, -10 // 2の補数表現(ビット反転 + 1) 神
bitSize: 8,
},
want: -10,
},
{
args: args{
a: 0b00001010, // 8bit, 10
bitSize: 8,
},
want: 10,
},
{
args: args{
a: 0b1110_00111000, // 12bit, -456??
bitSize: 12,
},
want: -456, // 0b0001_11000111 + 1 == 0b0001_11001000
},
{
args: args{
a: math.MaxInt32, // 32bit, max int32
bitSize: 32,
},
want: math.MaxInt32,
},
{
args: args{
a: 0b10000000_00000000_00000000_00000000, // 32bit, min int32
bitSize: 32,
},
want: math.MinInt32,
},
{
args: args{
a: 0b00000000_00000000_00000000_10000000,
bitSize: 8,
},
want: math.MinInt8,
},
{
args: args{
a: 0,
bitSize: 8,
},
want: 0,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got := SignExtend(tc.args.a, tc.args.bitSize)
if tc.want != got {
t.Errorf("(%#v) want %d but got %d", tc.args, tc.want, got)
}
})
}
}
@Code-Hex
Copy link
Author

Code-Hex commented Feb 9, 2022

最適解です

// SignExtend は a の値を bitSize で指定される値を解釈し、変換したものを返します。
// a の値は 32bit 以下の任意のbitSizeの値が入ってくる可能性があります。
// unsignedが入ってくる可能性は考慮されていません。(仕様外)
func SignExtend(a int32, bitSize uint32) int32 {
	// bitSize = 8 としての 0b11110110
	//
	// aに入ってくる実際の値は
	// 0b00000000_00000000_00000000_11110110
	//
	// tmp := 32 - bitSize
	// -> tmp := 32 - 8
	// -> tmp := 24
	//
	// a << tmp >> tmp はかっこをつけると (a << tmp) >> tmp
	// (a << tmp) の部分をさきに計算すると (a << 24) なので
	// 0b11110110_00000000_00000000_00000000
	//
	// つぎに 0b11110110_00000000_00000000_00000000 >> 24 する、が、一番左が 1 なので 1 が入る
	// 0b11111111_11111111_11111111_11110110
	//
	// https://yourbasic.org/golang/bitwise-operator-cheat-sheet/
	// https://www.interviewcake.com/concept/java/bit-shift#:~:text=Arithmetic%20Right%20Shifts
	//
	// https://go.dev/play/p/MbSqwCnK1QD

	tmp := 32 - bitSize
	return a << tmp >> tmp
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment