問題タブ [integer-promotion]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票する
4 に答える
7274 参照

c++ - ビット単位の演算子と整数の昇格で何が起こっていますか?

私は簡単なプログラムを持っています。サイズが 1 バイトの符号なし固定幅整数を使用していることに注意してください。

私の出力は次のとおりです。

より大きな数値をテストしたところ、オペレーター<<は常に正の数値を返しますが、オペレーター~は常に負の数値を返します。それから私は使用sizeof()して見つけました...

左シフトのビット演算子 ( <<) を使用すると、符号なしの 4 バイト整数が返されます。

ビット単位の not operator( ~) を使用すると、符号付き 4 バイト整数を受け取ります。

ビットごとの not operator( ~) は、算術演算子のように符号付き整数昇格を行うようです。ただし、左シフト operator( <<) は符号なし整数に昇格するようです。

コンパイラが背後で何かを変更していることを知る義務があると感じています。私の分析が正しければ、すべてのビット演算子は 4 バイト整数に昇格しますか? また、署名付きのものと署名なしのものがあるのはなぜですか? 私は困惑している!

編集:常に正の値または常に負の値を取得するという私の仮定は間違っていました。しかし、間違っていたので、以下の素晴らしい回答のおかげで、実際に何が起こっていたのか理解できます.

0 投票する
0 に答える
242 参照

c++ - 16 進定数による整数昇格

ここにいくつかのコードがあります:

それは「false」を出力します。foo には 0xE7 が含まれており、これは現在符号付き (-25) と見なされ、false を 0xE7 (10 進数で 231) と比較すると信じられるため、私はそれについてあまり心配していません。


しかし、これはどうですか?

それは「真」を出力します。

C++ 標準によると、次のようになります。

16 進数の整数リテラル (基数 16) は 0x または 0X で始まり、一連の 16 進数で構成されます。これには、10 進数の数字と、10 から 15 の 10 から 15 までの文字 a から f および A から F が含まれます。

と:

整数リテラルの型は、その値を表すことができる表 6 の対応するリストの最初のものです。

リストの最初の項目は「int」です。比較が真であるため、0xFFFFFFE7 == -25 と表示されます。


ただし、0xFFFFFFE7 は 4294967271 の別の書き方です。

それは「false」を出力します。したがって、0xFFFFFFE7 は 4294967271 と同じではありません。

標準に戻ると、「その値を表すことができる」という言葉は、実際には何を意味するのでしょうか? 明らかに、0xFFFFFFE7 を 4 バイトの signed int に詰め込むことができますが、それは実際には「値を表す」4294967271 ではありません。

でも:

また:

したがって、「その値を表すことができる」とは、「16 進数の定数を同等の 10 進数として扱う」のではなく、「2 進数レベルで」という意味のようです。これは正しいと思いますか?

gcc 4.8.2、Ubuntu 14.04、64 ビット プロセッサでテスト済み。

0 投票する
2 に答える
473 参照

c++ - 列挙型の整数昇格ルールをオーバーライドするには?

これは密接に関連しています C++ 列挙型は署名されていますか、または署名されていませんか? . JavaMan の回答によると、 anenumはどちらsignedでもありませんunsigned。しかし、それは不可欠な昇格規則に従います。

私はライブラリを使用して作業しており、それらを使用enumsして、ほとんどunsignedのタイプを期待する他のクラス オブジェクトに渡します (unsigned intや などsize_t)。-Wsign-conversion正当な間違いを見つけようとして警告を有効にすると、言語のルールが原因で多数の誤検知が発生します。

ルールのようなものは、型の安全性を確保し、一般的な間違いを見つけるのが難しい状況を生み出します。static_castコード全体に散らばるようなものは避けたいので難しいです。

enums具体的なsignedまたは型に昇格するための言語の既定の動作をオーバーライドする方法はありますunsignedか? char( is signed または unsignedを指定する方法と同様です)。


関連して、このライブラリは 1990 年代に作成されたため、多くの古いコンパイラをサポートしています。解決策が C++03 に対応し、場合によってはそれ以前にも対処できれば素晴らしいことです。

C++03およびC++11の移動コンストラクターを保護する方法から? 、実際には、他の C++ 言語バリアントが有効になっていることを検出する信頼できる方法がないことはわかっています。-std=c++03と を使用した Clang 3.5 でのテスト中に、顔が平らになり-std=c++11ました。

0 投票する
3 に答える
907 参照

c - Promotion when evaluating constant integer expressions in preprocessor directives - GCC

NOTE: See my edits below.

ORIGINAL QUESTION:

Came across some curious behaviour which I cannot reconcile:

When compiled:

This suggests that the preprocessor follows different type promotion rules when evaluating constant integer expressions. Namely that, when an operator has operands of mixed sign, the signed operand is changed to an unsigned operand. The opposite is (generally) true in C.

I can find nothing in the literature to support this, but it's possible (likely?) that I haven't been thorough enough. Have I missed something? Is this behaviour correct?

As it stands, it seems at though any conditional expression in an #if or #elif directive which involves an explicitly unsigned integer constant may fail to behave as expected, i.e. as it would in C.


EDIT: As per my comments in Sourav Ghosh's answer, my confusion originally stemmed from expressions which included constants specified with L and LL suffixes. The example code I included in my original question was too simplified. Here is a better example:

Building:

This seems to violate the clause in 6.3.1.8 subsequent to the one posted by Sourav Ghosh (my emphasis):

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

It seems to violate this clause because -5LL has a rank which is higher than 0uL, and because the type of the first (signed long long) can indeed represent all of the values of the type of the second (unsigned long). The catch is, the preprocessor doesn't know this.

As mentioned in https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_4.html (my emphasis):

The preprocessor calculates the value of expression. It carries out all calculations in the widest integer type known to the compiler; on most machines supported by GCC this is 64 bits. This is not the same rule as the compiler uses to calculate the value of a constant expression, and may give different results in some cases. If the value comes out to be nonzero, the `#if' succeeds and the controlled text is included; otherwise it is skipped.

What seems to be implied by "carries out all calculations in the widest integer type known to the compiler" is that the operands themselves are treated as though they are specified as that same 'widest' type. In other words, -5 and -5L are treated as though they are -5LL, and 0u and 0uL are treated as though they are 0uLL. This activates the clause quoted by Sourav Ghosh, and leads to the observed behaviour.

In effect, there is only one rank as far as the preprocesser is concerned, so type promotion rules which depend upon operands with different rank are ignored. Is this not indeed different from how the compiler evaluates expressions?


EDIT #2: Here's a real-world example of how the same expression is evaluated differently by the preprocessor than it is by the compiler (taken from Optiboot).

Building for an AVR target:

Note how F_CPU was specified as a signed constant.

This works as expected. Examining the object file:

... shows that the expected values are assigned. Namely, baud_setting gets 8, baud_actual gets 111111, and baud_error gets -3.

Now we build with F_CPU defined as an unsigned constant (as is customary on this target):

The reported error is of the wrong magnitude, and the wrong sign.

Examination of the object file shows it to be identical to the one built with a signed value for F_CPU.

None of this is a surprise now, with the understanding that the preprocessor treats all constants as either the signed or unsigned variant of the widest integer type.

The surprise is that this isn't explicitly mentioned in either the standard, nor the GCC docs (that I can find).

Yes, the C rules for evaluating operands are followed exactly by the preprocessor, but only insofar as the case where both operands of a binary operator are of the same rank. I cannot find any text in the standard which states that the preprocessor treats all constants specified with or without L or LL as though they were all LL before the rules for integer promotions specified in 6.3.1.8 are enforced, nor can I find any mention of this behaviour in the GCC docs. The closest is the passage from the GCC docs quoted above stating that the preprocessor "carries out all calculations in the widest integer type known to the compiler".

This does not (should not) explicitly mean that the operands are treated as though they were specified with suffixes designating them as the widest integer type known to the compiler. Indeed, absent an explicit passage on the subject, my expectation would be that the operands would be subject to the same type conversion and integer promotion rules to which all operands are subject when evaluated by the compiler. This doesn't seem to be the case. The implication, based on the tests above, is that the application of the normal C integer promotion rules comes after the preprocessor promotes the operands to the widest (signed or unsigned) integer type known to the compiler.

If someone can show any explicit and relevant text on this subject, either from the standard or the GCC docs, I'm interested.


EDIT #3: note: I've copied the below paragraphs from the comments section into the post itself, since there were too many comments for it to be seen.

If someone can show any explicit and relevant text on this subject, either from the standard or the GCC docs, I'm interested.

Here's some text from 6.10.1:

  1. For the purposes of this token conversion and evaluation, all signed integer types and all unsigned integer types act as if they have the same representation as, respectively, the types intmax_t and uintmax_t defined in the header <stdint.h>.

That would seem to clinch it.

0 投票する
3 に答える
400 参照

c - Char および int16 配列要素は両方とも 32 ビット 16 進数として表示されますか?

以下の例では:

出力:

と の両方に 32 ビット値が表示されるのはなぜcharですかint16_t? また、それらを比較して同じと見なすことができるのはなぜですか?

0 投票する
1 に答える
6148 参照

c++ - uint8_t 操作、いつオーバーフローしますか?

unsigned char を使用する場合、いつオーバーフローを心配する必要があるかわかりません。この場合は明らかです。

ただし、ここで何が起こるか:

減算を行う前に a と float の両方を float に変換しますか?

または、この場合も:

3 つの変数はすべて float に変換されていますか?

割り当てられている変数が float であっても、オーバーフローが発生する可能性はありますか? eがfloat、int、またはその他の場合、ルールは同じですか?

また、次のような場合はどうなりますか。

0 投票する
4 に答える
440 参照

c++ - 文字はC式で自動的に昇格されますか?

私は同僚に次のような声明を出しました。

「文字は C 式で自動的に整数に昇格されます。CPU は自然なワード サイズで最も速く動作するため、パフォーマンスには問題ありません。

char の昇格動作は、char のランクにより、標準のどこかに記載されていると思います。

これは私が戻ってきた応答です:

「文字はデフォルトで整数に昇格されません。レジスタのサイズは 32 ビットですが、コンパイラの実装として、行内の複数のバイト値を 1 つのレジスタにパックできます。これは常に予測できるとは限りません。自動昇格を確認できるのは、これは、C 標準がコール スタック メモリに 32 ビット値を公式に必要とするため、構造体にラップされていないときに型がコール スタックに渡されるときです. 多くの CPU アーキテクチャでは、32 ビット以外の値のアセンブリ呼び出しが最適化されているため、この場合、CPU またはコンパイラについて仮定を行うことができます。」

誰が正しいのか、何を信じればいいのかわかりません。事実は何ですか?

0 投票する
1 に答える
444 参照

c++ - サイズが int より小さいビットフィールドは、整数昇格の対象とすべきですか?

私が以下を持っているとしましょうstruct

私が興味を持っているのは表現のタイプですa + b。技術的には、ビットフィールドにはサイズが小さい「タイプ」がありますが、intおそらく整数の昇格が発生するはずであり、結果はintたまたまgccとclangにあるようです。

しかし、ビットフィールド自体の正確なタイプを抽出することは不可能であり、常にその「大きな」タイプ (つまりunsigned int、この場合) であると推定されるため、整数昇格が発生するのは正しいでしょうか? ビットフィールドの正確な型とそのサイズについて実際に話すことはできないため、unsigned int整数昇格が発生しないように推測される場合を除きます。

(繰り返しになりますが、私の質問は、MSVC がたまたまそれunsigned intがそのような表現のタイプであると考えているという事実から生じています)