83183


[JISX3010:2003 プログラム言語Ｃ](https://kikakurui.com/x3/X3010-2003-01.html)

> 6.3.1.3 符号付き整数型及び符号無し整数型 整数型の値を̲Bool型以外の他の整数型に変換する場合  
>  新しい型が符号付き整数型であって，値がその型で表現できない場合は，結果が処理系定義の値となるか，又は処理系定義のシグナルを生成するかのいずれかとする。
>
> 6.5.3.3 単項算術演算子  
>  単項-演算子の結果は，その（拡張された）オペランドの符号を反転した値とする。オペランドに対して整数拡張を行い，その結果は，拡張された型をもつ。

となっていますが、符号無し整数型の場合、符号の反転をどう解釈すればいいかが分かりません。

例えば、次のような操作をしても問題ないでしょうか？

[code]

    int main()
    {
        unsigned long long u = 1;
        long long l = -u; //-1になることを期待している
    }
    
[/code]

仮に`-u`がunsigned long longとして評価される場合、`-u == ULLONG_MAX`となり、`l =
-u`の操作でオーバーフローを起こしているかもしれないと思っています。  
`-(long long)u`のようにすれば問題ないかとは思うのですが、`-u`が符号無し、符号付きどちらとして評価されるのかが気になります。

符号無し整数型に単項演算子-を使用した場合、その値の型はどのように評価されるのでしょうか？よろしくお願いします。


CC BY-SA 4.0
2021-10-19T20:19:28.603
83178
2021-10-20T05:17:58.043
2021-10-20T00:04:20.727
3060
44647
question
c
単項演算子'-'を符号無し整数型に使用した場合について


まず式`-u`で整数拡張は起こりません。

（もし整数拡張が起こる式であったとしても、`unsigned long long`は`long
long`よりも大きい型ですから、整数拡張により`unsigned long long`が`long long`になることはありません）

そしてこの式の値は、`u`の2の補数となります。

式`l = -u`では`unsigned long long`を`long
long`の変数に代入しようとしています。ここで値が失われる可能性があります。そこで起こる結果は処理系によるかもしれませんが、`unsigned long
long`と`long long`とではサイズは同じなので、通常はビットパターンがそのまま保持され、最上位ビットが結果の符号となります。


CC BY-SA 4.0
2021-10-19T23:16:47.997
83182
2021-10-19T23:16:47.997
45045
83178
answer


引用はいいんようということなので以下条文等はすべて JIS X 3010:2003

6.5.3.3 は引用済みなので略。ここの解釈は `unsigned char` や `short` に単項 `-`
を適用する場合は整数拡張が発生して、演算結果は `int` になると読むべきです。 `unsigned long long`
は汎整数拡張の対象外なので文字通りに解釈され `-u` の型は `unsigned long long` のままです。

[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
言語仕様書には符号なし数を符号反転したらどう解釈されるかまで書いてありますが [c](/questions/tagged/c "'c'
のタグが付いた質問を表示") 言語仕様書はその挙動の解説を故意に行っていません。

> 6.5.16.1 単純代入 意味規則
> 単純代入は、右オペランドの型を代入式の型に型変換し、左オペランドで指示されるオブジェクトに格納されている値をこの値で置き換える。

ということなので型変換の記述を探すと 6.3 型変換 提示例に関係がありそうなのは

> 6.3.1.3 符号付き整数型および符号無し整数型  
>  新しい型が符号付き整数型であって、値がその型で表現できない場合は、結果が処理系定義の値となるか、または処理系定義のシグナルを生成するかのいずれかになる

ってことで代入の結果は処理系定義です（この例では厳密には代入でなく初期化なんだけど、やることは同じと解釈していいだろう）

まあ２の補数系整数を扱う処理系では（現存するすべての処理系といってよい）シグナルは発生せずに二進数表記したときのビットパターンが変化しない値になるだけです。

* * *

Visual Studio 2019 Version 16.11.5 の MSVC++ だと提示コードに対してレベル２警告 C4146
が発生します（標準設定である `/sdl` では C4146 は警告でなくエラーになる）悩ましいくらいなら最初から書かないほうがいいコードっス。

* * *

ちなみに [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") JIS X3014:2003 から

> 4.7 汎整数変換  
>  3 目的の型が符号付きの型の場合、その値が目的の型で表現できる場合には、値は変化しない。そうでない場合、結果の値は、処理系定義とする。

> 5.3 単項式  
>  符号なしの型の場合の符号反転の計算は `2 &sup{n};` からの減算によって行われる。ここで `n`
> は、昇格した演算対象のビット数とする。結果の型は、昇格された演算対象の型とする。

とあるので `unsigned long long` である `1ULL` を単項 `-` した結果は `unsigned long long`
の全ビットが立った値 (`0xFF..FFULL`) です。これは `long long` で表現できない値なので代入結果は処理系定義っス。結局のところ
[c](/questions/tagged/c "'c' のタグが付いた質問を表示") と [c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示") でこの例では挙動は変わらない＝二進数表記したときに同一のビットパターンとなる数が得られて `-1LL`
になるです。


CC BY-SA 4.0
2021-10-20T00:24:01.197
83183
2021-10-20T05:17:58.043
2021-10-20T05:17:58.043
8589
8589
83178
answer