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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C++11

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

Java

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

C++

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

Q&A

解決済

2回答

345閲覧

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

yumetodo

総合スコア5850

C++11

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

Java

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

C++

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

0グッド

1クリップ

投稿2018/05/30 16:02

編集2018/05/30 16:21

まずJavaのコードで

java

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

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

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

cpp

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

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

ところでWandboxで

cpp

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

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.

  1. 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の場合、結果の値は、変換元の対応する下位桁の値である。

具体的な例を示して説明する。

cpp

1// unsigned charが8ビット、unsigned intが16ビットとする 2 3int main() 4{ 5 unsigned int ui = 1234 ; 6 unsigned char uc = ui ; // 210 7}

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

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

結局のところ、

cpp

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

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


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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

C++

std::int32_t id = -104;
static_caststd::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もご参考に: * [interpret signed as unsigned](https://stackoverflow.com/q/1751346/684921) * [Using -1 as a flag value for unsigned (size_t) types](https://stackoverflow.com/q/22801069/684921) * [Is there a difference between -1 and ~0?](https://stackoverflow.com/q/24650019/684921)

投稿2018/05/31 02:24

編集2018/05/31 02:36
yohhoy

総合スコア6189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2018/05/31 02:41

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

0

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

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

投稿2018/05/30 16:50

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yohhoy

2018/05/31 04:43

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

2018/05/31 05:20

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

2018/05/31 07:53 編集

> 現実問題としては、処理系(ターゲット)はそれなりに想定しておかないとまともにコードを書けないのが実情ではないでしょうか。 はい。どこかで線引きは必要ですし、仰るラインは合理的と思います。 > また、質問内容はエンディアンに関するもの 真意はyumetodoさんしか分かりませんが、私は「エンディアン云々は単なる前置き実験で余談。主題はsigned→unsigned変換結果の保証度合い」と解釈していました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問