質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.75%

  • Java

    13145questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • C++

    3244questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • C++11

    107questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

小さい符号なし整数型への変換の詳細な仕様

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 325

yumetodo

score 1996

まずJavaのコードで

int id = -104;//152でも同じ
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(id);
buffer.array()[3]; //=> -104

というのがあります。これをC++11に移植することを考えています。

Javaがビッグエンディアンであることを踏まえて、リトルエンディアンな環境を仮定すれば

std::int32_t id = -104;
reinterpret_cast<std::uint8_t*>(&id)[0];// => 152

と書けそうですが、できればリトルエンディアンな環境を仮定したくないですし、かといってそもそもエンディアンの判定自体やりたくないです。

ところでWandboxで

#include <cstdint>
#include <iostream>
int main()
{
    std::int32_t n = -104;
    std::uint8_t n2 = reinterpret_cast<uint8_t*>(&n)[0];
    std::cout << int(n2) << ' ' << int(static_cast<uint8_t>(n)) << std::endl;// => 152 152
}

https://wandbox.org/permlink/jzFflPh8RZSFWOjI
というコードを走らせるとstatic_cast<uint8_t>(n)でもいい気がします。

しかしこれはどういう仕様で変換されるのか理解できていません。

N3337の§4.7 [conv.integral]には

1. A prvalue of an integer type can be converted to a prvalue of another integer type. A prvalue of an unscoped
enumeration type can be converted to a prvalue of an integer type.
2. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source
integer (modulo 2 n where n is the number of bits used to represent the unsigned type).

とありますが、イマイチ理解できず、江添本では

変換先の整数型がunsignedの場合、結果の値は、変換元の対応する下位桁の値である。
  具体的な例を示して説明する。
 

// unsigned charが8ビット、unsigned intが16ビットとする

int main()
{
    unsigned int ui = 1234 ;
    unsigned char uc = ui ; // 210
}

この場合、unsigned int型は、16ビット、uiの値は、2進数で0000010011010010である。unsigned char型は8ビット。つまり、この場合の対応する下位桁の値は、2進数で11010010(uiの下位8ビット)である。よって、ucは、10進数で210となる。 

とありますが、例がunsigned -> unsignedでsigned -> unsignedへの言及がありません。

結局のところ、

std::int32_t id = -104;
static_cast<std::uint8_t>(id);// => 152

は常に期待できる動作なのでしょうか?


C++ における整数型の怪と "移植性のある" オーバーフローチェッカー (第1回 : 整数型の怪と対策の不足) - Qiita#C 言語 (C99) における整数型の定義
通常の整数型とは別に、stdint.h の int8_tint16_tint32_tint64_t といったビット固定の符号付き整数型が存在する場合があります。これらのビット数固定符号付き整数型に限っては、存在すれば必ず 2 の補数表現であることが保証されており、データ型の移植性を飛躍的に高めることができます。

質問の書き方だと2の補数表現な処理系を暗黙に仮定していましたが、仮定していいことにします

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

std::int32_t id = -104;
static_cast<std::uint8_t>(id);// => 152


は常に期待できる動作なのでしょうか?

私の仕様解釈では「はい」。

C++17 §7.8 Integral conversions [conv.integral]/paragraph 2は、任意の 変換元整数から符合無し(unsigned)整数への変換を規定します。変換元が符号付き整数std::int32_tの場合、変換元整数値と “2^nを法として合同な” 最小の符合無し整数std::uint8_tへと変換されます。値-104 (0b1...10011000) は、2^8を法として 値152 (0b10011000) と合同です。

同Noteは、符号付き整数が2の補数表現の場合、ビット幅切詰めが生じない限り、この変換でビットパターンが保持されるという(ある意味自明な)補足説明になっています。

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type). [Note: In a two's complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). -- end note]

(Teratail/Markdownの制約から 2のn乗 を 2^n と表記しています)

下記StackOverflowのQ&Aもご参考に:

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/31 11:41

    2の補数表現な処理系を仮定しなくても良さそうですね。

    キャンセル

0

期待できますし、そうなってもらわないと困ります。
符号の有無は最上位ビットの扱いの問題なので、エンディアンの文脈では関係ないと思います。また、エンディアンはあくまでもメモリ配置の問題であって、演算の過程ではエンディアンの違いによる差異はありません。
C/C++の仕様のレベルでは確認はしていないのですが、少なくとも私の知っているCPUではレジスタに32bit値として読み込んだ値を8bit値として利用する場合は最下位バイトが使われます。

もし、期待通りにならないと移植性を保つことが非常に困難となります。それをC/C++が許容しているとは思えません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/31 13:43

    FYIというか残念情報ですが、C/C++が言語仕様として"保証"する範囲は、(私も含めた)普通のプログラマが普通にプログラミングを行ううえでは非常に乏しいものです。

    今回のケースで言えば、unsigned型への変換は別回答の通りですが、signed型への変換は処理系定義とされており、期待するような移植性担保は行ってくれません。例えば sizeof(int)==4, sizeof(short)==2 な環境で
    int x = -100000;
    short y = x;
    というコードがC/C++言語仕様として有効(valid)な処理であることまでは保証しますが、その実行結果については何も規定しません。起こり得る選択肢として「処理系定義の何らかの値になる、もしくは処理系定義の何らかの例外が発生する」とだけ規定されます。

    キャンセル

  • 2018/05/31 14:20

    その手の話題に持っていくのであれば、我々プログラマーが言語仕様をどこまで厳格に守り、あるいは移植性をどこまで考慮すべきか、という議論が必要ですね。charが8bitでない処理系の考慮もすべきと考えなければならないのであればおっしゃる通りでしょう。ですが、現実問題としては、処理系(ターゲット)はそれなりに想定しておかないとまともにコードを書けないのが実情ではないでしょうか。質問者さんも処理系を仮定することを許容するのはそういうことだと思います。また、質問内容はエンディアンに関するもので「リトルエンディアンだとこうだけどビッグエンディアンではどうなのか?」といった趣旨であり、符号の扱いに関する処理系依存の問題ではないと受け取りました。

    とはいえ、

    > それをC/C++が許容しているとは思えません。

    これに関しては誤りでした。コンパイラー開発者は許容しないかもしれませんが、C/C++の仕様としては許容されてしまっていますね。

    キャンセル

  • 2018/05/31 16:52 編集

    > 現実問題としては、処理系(ターゲット)はそれなりに想定しておかないとまともにコードを書けないのが実情ではないでしょうか。

    はい。どこかで線引きは必要ですし、仰るラインは合理的と思います。

    > また、質問内容はエンディアンに関するもの

    真意はyumetodoさんしか分かりませんが、私は「エンディアン云々は単なる前置き実験で余談。主題はsigned→unsigned変換結果の保証度合い」と解釈していました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.75%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    13145questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • C++

    3244questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • C++11

    107questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。