0

はい、わかっています。ダブル値でビット単位の操作を行うのは悪い考えのように思えますが、実際には必要です。

私の質問のために次の段落を読む必要はありません。

Mozilla Tamarin (Actionscript Virtual Machine) の特別な mod を実際に試してみました。その中で、どのオブジェクトにもそのタイプ用に予約された最初の 3 ビットがあります (たとえば double は 7 です)。これらのビットは、プリミティブ データ型 (int のみ 29 ビットなど) の精度を下げます。私の mod では、この領域を 2 ビット拡張する必要があります。つまり、たとえば 2 つの double を追加する場合、これらの最後の 5 ビットを 0 に設定し、計算を行ってから結果をリセットする必要があります。なぜそんなに^^

コードに戻ります。非常によく似た問題を示す最小限の例を次に示します。

double *d = new double; 
*d = 15.25; 
printf("float: %f\n", *d);

//forced hex output of double
printf("forced bitwise of double: ");
unsigned char * c = (unsigned char *) d;
int i;
for (i = sizeof (double)-1; i >=0 ; i--) {
     printf ("%02X ", c[i]);
}
printf ("\n");

//cast to long long-pointer, so that bitops become possible
long long * l = (long long*)d;
//now the bitops: 
printf("IntHex: %016X, float: %f\n", *l, *(double*)l); //this output is wrong!
*l = *l | 0x07; 
printf("last 3 bits set to 1: %016X, float: %f\n", *l, *d);//this output is wrong!
*l = *l | 0x18; 
printf("2 bits more set to 1: %016X, float: %f\n", *l, *d);//this output is wrong!

これを VisualStudio2008 で実行すると、最初の出力は正しくなります。セカンドも。3 番目は、16 進表現と浮動小数点表現の両方で 0 を返しますが、これは明らかに間違っています。4 番目と 5 番目も 16 進数と浮動小数点数の両方でゼロですが、変更されたビットは 16 進数値に表示されます。だから私は、タイプキャストがここで物事を台無しにしたのではないかと思いました。さらに2つの出力:

printf("float2: %f\n", *(double*)(long long*)d); //almost right
printf("float3: %f\n", *d); //almost right

15.25 と表示されていますが、15.2500000000000550670620214078 のはずです。だから私は、ちょっと、それは出力の精度の問題だと思った。もう少し上に変更しましょう:

*l = *l |= 0x10000000000;
printf("float4: %f\n", *d);

繰り返しますが、出力は 15.2519531250000550670620214078 ではなく 15.25(0000) です。奇妙なことに、別の強制的な 16 進出力 (上記のコードを参照) では、d がまったく変更されていません。少しいじってみたところ、ビット 31 (0x80000000) が手動で設定できる最後のビットであることがわかりました。そして聖なるモリ、それは実際に出力に影響を与えます(15.250004)!

そのため、少し迷いましたが、まだ多くの混乱があります。printfは壊れていますか?ここでビッグ/リトルエンディアンの混乱がありますか? ある種のバッファオーバーランを誤って作成していますか?

誰かが興味を持っている場合、元の問題 (タマリンのこと、上記を参照) では、ほとんど逆です。そこでは、最後の 3 ビットが既に設定されています (これは double を表します)。それらをゼロに設定すると正常に機能します(これが元の実装です)。さらに 2 を 0 に設定すると、上記と同じ効果があります (全体の値がゼロになります)。ちなみに、これは出力固有ではありませんが、数学演算もこれらのフロア値で機能するようです(そのように取得された2つの値のmulは0になります)。

どんな助けでも大歓迎です。ご挨拶。

4

3 に答える 3

4

15.25 と表示されていますが、15.2500000000000550670620214078 のはずです。

デフォルトでは、 %f6 桁の精度で表示されるため、違いはわかりません。また、修飾子を使用して、最初の引数がlong longではなくであることを指定する必要があります。そうしないと、ガベージが出力される可能性があります。これを修正して などのより高い精度を使用すると、期待どおりの結果が得られるはずです。intll%.30f

printf("last 3 bits set to 1: %016llX, float: %.30f\n", *l, *d);
printf("2 bits more set to 1: %016llX, float: %.30f\n", *l, *d);

last 3 bits set to 1: 0000000000000007, float: 15.250000000000012434497875801753
2 bits more set to 1: 000000000000001F, float: 15.250000000000055067062021407764

もう少し上に変更しましょう:

*l = *l |= 0x10000000000;
printf("float4: %f\n", *d);

未定義の動作をするローグがある=ため、値が変更される場合と変更されない場合があります (また、プログラムがクラッシュしたり、ピザを求めて電話をかけたり、宇宙を破壊したりする場合もあります)。また、コンパイラが C++11 に準拠していない場合、整数リテラルの型は より大きくなくlong、32 ビットしかない可能性があります。その場合、(おそらく)ゼロになります。

それらを修正すると(私の場合は、コードをそのまま使用して)、期待される結果が得られます。

*l = *l | 0x10000000000LL;  // just one assignment, and "LL" to force "long long"
printf("float4: %f\n", *d);


float4: 15.251953

これがデモンストレーションです。

于 2012-07-27T10:46:18.737 に答える
0

printf のパラメータに誤りがあります。8 バイトの値を渡す場合は、%x の代わりに %llx を使用する必要があります。

使用する

printf("last 3 bits set to 1: %llX, float: %f\n", *l, *d);
*l = *l | 0x18; 
printf("2 bits more set to 1: %llX, float: %f\n", *l, *d);

そしてあなたのコードは動作します

于 2012-07-27T10:35:45.107 に答える
0

32 ビットでは、定数を long (32 ビット) よりも大きくすることはできないため、それを行うことはできません。

*l |= 0x10000000000;

変数を作成してからシフトする必要があります。

long long ll = 1;
ll <= 32;
*l |= ll;
于 2012-07-27T10:46:08.473 に答える