dポイントプレゼントキャンペーン実施中!

算術右シフトと論理右シフトをC言語にて実装しようとしています。
『ハッカーのたのしみ』にある論理右シフトを用いて算術右シフトを作る方法を使おうと思っているのですが、C言語で確実に論理右シフトするにはどうすればよいのでしょうか。

int x;
(unsigned int) x >>= n;
とするとgccではゼロ拡張してくれるようなのですが、
use of cast expressions as lvalues is deprecated
というWarningが出ます。
この方法が確実とは思えませんし、
unsigned int xx;
xx = x;
も処理系依存と聞きます。
出来ればWarningが出ない形で解決したいのですが、どうすればよいのでしょうか。
教えてください。

A 回答 (6件)

ああ, よく考えれば「最初から unsigned で全部処理をする」というのが最も正しい解のような気がします.


それはさておき, 右シフト演算子の左オペランドが signed で負数の場合, 結果は「実装定義」です. 未定義ではありません.
ついでにいうと, signed → unsigned のキャストは 6.3.1.3節のパラグラフ2 にあるように変換されます. 本当は JIS を参照するのが正しいんだけど ISO しか持ってないのでこっちを参照すると
If the new type is unisgned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
となっていて, signed int → unsigned int の場合には実質的に「もともとが正ならそのまま, 負なら UINT_MAX+1 を加えた値」となります. ということで 2の補数を使っていればたいていはビットパターンが変化しません.
逆に unsigned → signed の場合は「signed で表現できればそのまま, 表現できなければ実装定義」となります.
ちなみに C++ だと signed → unsigned の変換は「2^n を法として最小の整数」で, 2の補数を使ってる場合には同じことですね.
ん~, 「unsigned int xx; xx = x; も処理系依存」というのも, 本当にここまで考えて書いているのかなぁ....
    • good
    • 0
この回答へのお礼

しつこく付き合わせてしまい申し訳ありません。
未定義ではなく実装定義ですね。
日本語が理解できていなかったようです orz。
unsigned int xx;
xx = x;
はxとxxのビットパターンが同じになるかどうか不安という意味で書きました。
今回のことで、一度仕様書を通読する機会を持つ必要を感じました。
土方志望では無いのですが、せめて人並みには使えるように頑張ろうと思います。

お礼日時:2007/02/22 00:35

案外、共用体を使って、



typedef union
{
 int s;
 unsigned u;
} xx;
xx.s = x;
xx.u >>= n;
x = xx.s

とするのが一番楽な気がします。
もちろん、もともと全部unsignedで扱えるのであれば、その方が簡単ですが。
    • good
    • 0
この回答へのお礼

ありがとうございます。
なるほど、それならば安心して書けます。
目から鱗でした。

お礼日時:2007/02/22 01:02

Warningが出るのは、左辺値をcastしているからだと思います。


質問の実装をするのであれば、

int x;
unsigned int x_tmp;

x_tmp = (unsigned int)x;
x_tmp >>= n;

としてみては如何でしょうか。

ただ、m_11さんがおっしゃるとおり、処理系に依存するので、必ずしも成功するとは限りません。
    • good
    • 0
この回答へのお礼

左辺値をキャストすれば怒られるのも当然でした。
ご指摘頂きありがとうございました。

お礼日時:2007/02/21 23:24

あれ? 真意が伝わっていない気が....


ISO C の規格によれば, (右オペランドが非負かつ左オペランドのビット幅未満の値を持っているという前提のもとで) 左オペランドが unsigned なら全ての処理系において予測可能な結果となりますが, signed の場合には (右シフトかつ負のとき) 実装定義の結果となったり (左シフトでその結果が表現できる値を越える場合には) 未定義動作となります.
ということで, 左オペランドを signed のまま扱うのは危険ですからなんとかして unsigned にする必要があります.
これは, 普通の環境では「単に unsigned にキャストするだけ」で実現できます. 正確には, 「signed の表現が全ビット幅を用いた 2の補数表現」かつ「可能な全ビットパターンに対して unsigned で値が有効」であれば実現可能です.
ということで, 普通の環境であれば
x = (int)((unsigned)x >> n);
で x の値を右に nビット論理シフトすることができます.
普通じゃない環境, 例えば負数を表現するのに「1の補数」を使ってみたり「符号+絶対値」であったりするような環境ではうまくいかないので, そういう環境 (現存するかどうかは知りません) も考慮するなら「努力と根性」が待っています. もちろんこんな環境を考慮するくらいなら
x >> 1
すら危険ですが.
    • good
    • 0
この回答へのお礼

signedで負数を右シフトしたときの結果は"未定義"だったのですね。
算術シフトか、論理シフトのどちらかになるものと勘違いしていました。

さすがに、負数表現に2の補数を用いない機械を相手にする気はありませんので、
x = (int)((unsigned int) x >> n);
で良いと聞いて安心しました。
かなり広範囲の環境で、unsignedへのキャストはキャスト前と同じビットパターンになることが確実との情報も有益でした。
ありがとうございました。

お礼日時:2007/02/21 23:22

1ビット右シフトした結果に対して、0x7fffffffでANDをとられてはいかがでしょうか。

    • good
    • 0
この回答へのお礼

なるほど、
上記の操作をした後、残り分をシフトすれば確実に論理右シフトしたことになりますね。
目から鱗でした。
ありがとうございます。

お礼日時:2007/02/21 20:55

う~ん, ひたすら頑張るんだったら x が正か負かで場合分けせざるをえないんじゃないかなぁ. 普通の環境なら


x = (int)((unsigned)x >> n);
で十分だろうけど.
    • good
    • 0
この回答へのお礼

ありがとうございます。
やはり、1本ずつビットを下ろして行くしかないのでしょうか。

お礼日時:2007/02/21 20:47

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!