2

私はCでのキャストをよく理解していません.誰かが本のコンピュータシステムの質問で私を助けてくれますか: A Programmer's Perspective :


任意の整数値 x、y、z を生成し、次のように double 型の値に変換します。

int x = random();
int y = random();
int z = random();

double dx = (double) x;
double dy = (double) y;
double dz = (double) z;

次の C 式のそれぞれについて、式が常に 1 を返すかどうかを示す必要があります。常に 1 を返す場合は、基礎となる数学的原理を説明してください。そうでなければ、結果が 0 になる引数の例を挙げてください

A. (float) x == (float) dx
B. dx - dy == (double) (x-y)
C. (dx + dy) + dz == dx + (dy + dz)
D. (dx * dy) * dz == dx * (dy * dz)
E. dx / dx == dz / dz
4

3 に答える 3

3

キャスティングとは、あるものから別のものに変換することです。16 ビットまたは 32 ビットに対する 8 ビットの整数である可能性があります。

unsigned int x;
unsigned char y;

y = 7;
x = (unsigned int) y;

x=y; を実行した場合、これはもちろん暗示されます。ここでは、8 ビットと 32 ビットの typedef を想定しています。ビット パターン 0x07 から 0x00000007 になります。

これは実際にはとても楽しいエクササイズです。フォーマットを作成してから、質問について考えてみましょう。

浮動小数点形式は通常、このようなことを行います。純粋に正の数で考えて、この演習のほとんどを行うことができます。底が 10 の数 1234 を取り上げます。小学校での科学的表記法について話します。これは 1.234 の 10 の 3 乗になります。ベース 2 コンピューター形式は、浮動小数点と呼ばれる理由があって同じように機能します。小数は仮想的な意味で移動します。したがって、float の 9 番目の 0b1001 は、最上位のものを見つけて、その後に 1.001 かける 2 の 3 乗です。17 の数字 1.0001 かける 2 の 4 乗です。浮動小数点は符号をカバーする必要があります。整数全体でも分数を強制しているため、「分数」または仮数にはいくつかのビットが必要です。そして指数。一部の形式で想定できる小数点の前の値を保持する必要は必ずしもありません。もちろん、ゼロは特別な問題であり、浮動小数点形式には特別な例外またはパターンが必要です。この形式には、適用される 2 の累乗を表すビット数も必要です。手始めに、正の整数のみが生成され、整数が 8 ビットであるため、0 から 0xFF が整数の全世界であると仮定します。double 形式には小数部のビットが 12 あり、single には 5 があります。手始めに、正の整数のみが生成され、整数が 8 ビットであるため、0 から 0xFF が整数の全世界であると仮定します。double 形式には小数部のビットが 12 あり、single には 5 があります。手始めに、正の整数のみが生成され、整数が 8 ビットであるため、0 から 0xFF が整数の全世界であると仮定します。double 形式には小数部のビットが 12 あり、single には 5 があります。

1.111111100000 の 2 の 7 乗を 12 の小数ビットで簡単に表すことができる最悪のケースの数値 0xFF は何であり、この演習全体をカバーするのに十分な数の指数ビットがフォーマットにあると仮定します。

だから私たちの double は C で私たちの整数のすべてを保持することができます

dx = ダブル (x);

ビットパターン 00001001 で開始した場合はフォーマットを変換することを意味します。これは数字の 9 であり、2 の 3 乗の 1.001000000000 倍になる構成された double では、フォーマットに保存するビットがさらに多くなりますが、この質問には関係ありません。 .

私たちの作ったシングルでは、9 は 1.00100 かける 2 の 3 乗です。

しかし、単精度の数値 0xFF は 1.11111 かける 2 の 7 乗です。これを整数に戻すと、0xFF ではなく 0xFC になり、変換でいくつかのビットが失われます。丸めなしと仮定します。基数 10 では、1/27 は 0.0307307307 です...これを 4 桁で切り捨てると、0.0307 になり、少し低くなります。しかし、丸めで 0.031 を 3 にすると、少し高すぎます。シングルで 0xFF を見ると、1.11111 であり、次の 2 ビットが投げられて 11 であり、半分以上であるため、10.00000 を 2 の 7 乗に切り上げると、1.00000 の 2 の 8 乗に正規化されます。0xFF は基本的に 0x100 に丸められます。0xff を表す 0x100 または 0xFF を表す 0xFC を使用することもできますが、正確ではありません。逆に変換すると、少し高いか、または少し低くなります。

したがって、最初のケース A (float)x vs (float)((double)x) を見てください。1.00100 かける 2 の 3 乗 vs 1.001000000000 かける 2 の 3 乗。 float x と double x をカバーする場合、double を変換する必要があります。 、浮動小数点形式間で整数から浮動小数点として同じクリッピングと丸めが使用されていますか? ハードウェアによって異なりますが、11111111 が 1.111111100000 と同じように変換されることを期待できますが、理想的な世界では変換されない可能性があります。

C は興味深いケースで、次のように言えます。正の数 最悪の場合 3 + 3 = 6 それをスケールアップするビット数 0xFF と 0xFF では、倍精度形式で 12 を超えるビット数は何ですか? 0xFF と 0xFF と 0xFF を足すと、何ビットかかりますか? 12以上ですか?グループ化を再配置すると、それが変わりますか?

D 2 ビット数 3*3 = 9 オペランドごとに 2 ビット入力 アウトは何ビット? 0xFF×0xFF? 次に、0xFF x 0xFF x 0xFF は 12 ビット以上かかりますか? 最初の質問です。2 番目の質問である場合、クリッピングと丸めはどのように機能しますか? グループ化によってクリッピングと丸めが影響を受けます。ここまでは頭脳派です。理解するには簡単なプログラムを書かなければならないかもしれません。

そして、Eは最初に考えたほど厄介ではなく、読み直しました。ビットパターンを正確なビットパターンで分割しています。コンピューターだけでなく、一般的に除算に関する最大の問題は何ですか?どの整数が私たちに問題を与え、他の問題を引き起こしますか? ここで、正の x を正の x で割った値と負の z を負の z で割った値に符号付きの数値を許可するとどうなるでしょうか?

ウィキペディアで倍精度浮動小数点を検索してから、単精度を検索してください。double にはいくつの「小数」ビットがありますか? そして、32 ビットまたは 31 と int の符号を仮定すると、すべての有効数字が適合しますか? 正の数を考えると、31 の 2 の累乗になるので、31 を表すのに多くの指数ビットが必要になるでしょうか? 十分ですか?分数に31ビットまたは30ビットの有効ビットを保持できますか?指数でプラスまたはマイナス 31 の累乗を表すことができますか?

編集

そこでケースを考えて DI はプログラムを書きました。私は小数部の8ビットを使用しました

//5 9 f 020140 030120 0301E0 090151 090152
(5 * 9) * 15 vs 5 * (9 * 15)

5 は 1.01000000 または 0x1.40、9 は 1.00100000 または 0x1.20、0xF は 1.11100000 または 0x1.E0 です。

float での乗算は、あなたが考えるのと同じではありません。5 かける 9 を直接乗算していないため、頭が少し痛くなります。1 になるようにすべてをシフトしました。

0x120 * 0x140 = 0x16800

その正規化のために、8 ビットを切り捨てます。これは、後で説明するように丸めた場所です。この場合、結果は 0x168 であり、正規化は必要ありません。

5*9 = 0x2D = 101101
1.01101000 0x1.68

指数を気にする必要はありませんが、5 の指数は 2、9 の指数は 8 であるため、結果は指数が 5 の 0x168 になるため、0x168 に 0x1E0 = 0x2A300 を掛けると、性質上 8 ビット 0x2A3 が即座に切り捨てられます。乗算の。ここで、2.something ではなく 1.soemthing が必要なので、右にシフトして指数を増やします。指数は 5+3=8 です。もう 1 つ 9 を付けますが、0x2A3 0x1010100011 に注意してください。 0x1.51.51 ビットの半分の代わりに、基数 2 の浮動小数点の性質である 0x1.51 があります。さて、それは答えを切り上げることになっていますか?多分。もしそうなら、答え0x1.52

Take it the other way 5*(9*15)
0x120*0x1E0 = 0x21C00
or 0x21C  1000011100
0x10E
0x140*0x10E = 0x15180
we are going to lose a bit here
is it 0x151 or 0x152?

そして、これらは同等の丸めの質問ですか? 両方のパスの結果が 0x152 になり、それらが等しくなりますか?それとも、ビットを切り落とす 1 つのビューが他のパスとは異なりますか? まったく丸めずにクリップするだけの場合、両方の答えは 0x152 になります。

3 11 1f 010180 040110 0401F0 0A018B 0A018A
(3*17)*31 vs 3*(17*31)  no rounding just clipping
(3*17)*31
0x180*0x110 = 0x19800
0x198
0x198*0x1F0 = 0x31680  0x316 0x1100010110
0x18B
3*(17*31)
0x110*0x1F0 = 0x20f00
0x107
0x180*0x107 = 0x18A80
0x18A
0x18B != 0x18A

1 つのパスを 2 ビット切り取り、もう 1 つのパスを 1 つだけ切り取りました。それは公平でしたか?全体として取られた 0x31680

110001011010000000
110001011 010000000 with discarded bits on the right

そのように見ると、01 または 010 または 0100 は半分未満であり、3 または 2 の切り上げよりも切り上げられません。1.33 は、基数 10 で 1.4 に切り上げられません。

しかし 0x20F00

100000111100000000
100000111 100000000

これは中間点 1/10、10/100、100/1000 で、半分です。

それは0x108だったはずですか?

0x180*0x108 = 0x18C00
0x18C
0x18C != 0x18B

したがって、丸めをそのように表示しても、順序が一致しないため、違いが生じます。

丸めが間違っていると思うかもしれませんが、それは公平です。もしそうなら、可能なすべての整数パターンが機能しますか? int が 32 ビットで、double が 52 ビットの仮数部を持つ IEEE754 であると仮定すると、オーバーフローしてビットを切り落とす必要があるため、切り刻みと丸めが発生します。順序は重要ですか?

于 2016-12-28T04:57:55.110 に答える
2

C は、多くのプラットフォーム (あらゆる種類のプロセッサやコンピュータ) にとって非常に移植性の高い言語です。5 つの同等性テストの結果は、プラットフォームごとに異なります。C仕様を見てみましょう。


int x = random();次の各テストについて、「結果はどのように動作しますintか?」と言っているようなものです。詳しく見てみましょう。

random()標準 C ライブラリ関数ではありません。「ランダム」という名前は、関数の結果が範囲全体であることを示唆していintます:[INT_MIN...INT_MAX]または[0...INT_MAX]. どちらかで行ってください。

================================================== ===================

int
C は、 の最小範囲intが -(2 15 -1) から -(2 15 -1) または -32,767 から 32,767 であることを指定します。
すべてではありませんが、通常、プラットフォームは -(2 31 ) から -(2 31 -1) または -2,147,483,648 から 2,147,483,647 の範囲を使用します。
-(2 63 ) から (2 63 -1) または-9*10 18から 9*10 18の範囲を使用する人もいます。
C は、範囲の最大サイズを指定しません。

double
C は、 の最小範囲doubleが -10 37から 10 37であることを指定します。
すべてではありませんが、通常、プラットフォームは -1.8*10 308から 1.8*10 308の範囲を使用します。
C は、範囲の最大サイズを指定しません。

doubleは浮動小数点です。特定の精度までの数値を表します。範囲の大きな整数がすべて表現できるわけではありません。

C は、正確に表現可能な整数の最小範囲が -10 9から 10 9であることを指定します。 通常、この正確に表現可能な整数の範囲は、約 -9*10 15から 9*10 15です。 double

IEEE タグは、 binary64が使用されていることを示唆しています。それを使用することもできますが、重要な問題は幅intの範囲とdouble正確に表現可能な整数であるため、FP の選択によって結果が大きく変わることはありません。どちらか一方が他方よりも広い場合があります。 int一般的にはもっと狭いです。


ではdouble dx = (double) x、多くのプラットフォームで正確に変換されます。の範囲が aの正確に表現可能な整数intの範囲よりも広いまれなプラットフォームでは、正確ではありません。double

を使用すると、の範囲を超える(float) x多くのプラットフォームで変換が不正確になるのが一般的です。intfloatdouble

orintへの不正確な変換が発生した場合、結果は通常、可能な限り最も近い結果になります。floatdouble


(float) x == (float) dx浮動小数点の範囲が と の正確に表現可能な整数と異なり、両方が の部分範囲であるプラットフォームでは、 は false になる可能性がありfloatます。おそらく失敗します。binary64 と 32 ビット(OP の場合?) では、この等価性は常に真になるはずです。doubleintINT_MAXint

dx - dy == (double) (x-y)上記と同じ理由で false になる可能性があり、追加の理由もあります。C では、int オーバーフローは定義されていないため、定義されていませんINT_MIN - 1。あらゆる結果が可能です。

(dx + dy) + dz == dx + (dy + dz)int範囲がより広い場合double(一般的ではない)はfalse になる可能性がありdx、表現可能な次の値が 2 より大きい丸められた値です。 (dx + 1)に丸めることができますがdxdx + 2表現可能です。そう(dx + 1) + 1 != dx + (1 + 1)

(dx * dy) * dz == dx * (dy * dz)サブ積および/または積が正確でない場合、 は簡単に偽になります。多くの場合、数学的積が の正確に表現可能な整数を超える場合ですdouble

dx / dx == dz / dzdxが 0 による除算が0定義されていない場合、単純に false にすることができます。

于 2016-12-28T10:50:58.353 に答える