読者です 読者をやめる 読者になる 読者になる

移植性が(ほとんど)あり性能問題が無い(かもしれない)、負の値に対応した算術右シフトのCコード

普通は、自分のマシンでどのようなコードが生成されるかを確認して、うまくいっていればそれで良しとする所ですが、右シフトは凝ったコードを書こうとするCプログラマにとって悩みの種です。

しかし、手元の環境( FreeBSD clang version 3.4.1 )で、次のような、「あらゆる環境で2の補数マシンをエミュレーションするコード」を、

(追記: ビットパターンが 0x800...000 のようになるような最小の負の値の時には、このコードでも「未定義」を踏んでいます)と思ったのですが、最初に +1 することで回避してますねコレ。それでも未定義に落とせる抜け穴あるかな?

int
signed_shift_right(int x, unsigned a)
{
    return ( (x < 0) ? (-(-(x+1)>>a)-1) : (x >> a) ) ;
}

-O3 オプション付きでコンパイルしてみたところ、次のように「完全に」最適化されました。

$ clang -W -Wall -Wextra -pedantic -O3 -S signed_shift_right.c
$ cat signed_shift_right.s
	.file	"signed_shift_right.c"
	.text
	.globl	signed_shift_right
	.align	16, 0x90
	.type	signed_shift_right,@function
signed_shift_right:                     # @signed_shift_right
	.cfi_startproc
# BB#0:
	pushq	%rbp
.Ltmp2:
	.cfi_def_cfa_offset 16
.Ltmp3:
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
.Ltmp4:
	.cfi_def_cfa_register %rbp
	movb	%sil, %cl
	sarl	%cl, %edi
	movl	%edi, %eax
	popq	%rbp
	ret
.Ltmp5:
	.size	signed_shift_right, .Ltmp5-signed_shift_right
	.cfi_endproc


	.ident	"FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512"
	.section	".note.GNU-stack","",@progbits

static inline を付けて、インライン展開まで効けば、おそらく完全にオーバヘッド0のコードが生成されるものと思われます。