パスカルの答えはOKですが、一部のユーザーには分かりません。下位レベルでどのように見えるか興味があれば(コプロセッサで、ソフトウェアで浮動小数点演算を処理していないと仮定して)、読んでください。フロートの32ビット(IEEE 754)は、内から整数のすべてを格納することができる[-2 ... 2 ]範囲で
。範囲外の整数も浮動小数点数として表現されていてもかまいませんが、すべてが浮動小数点数ではありません。問題は、浮動小数点数で演奏するのに24の重要なビットしか持てないことです。ここで
INT-からどのように変換することです>フロートは、典型的には、低レベルで次のようになります。
fild dword ptr[your int]
fstp dword ptr[your float]
それは2つのコプロセッサ命令の単なるシーケンスです。最初に32ビットintをコンパイラのスタックにロードし、それを80ビット幅のフロートに変換します。
インテル®64およびIA-32アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル
(X87 FPUを使用したプログラミング):
浮動小数点、整数、またはパックBCD整数 の値がメモリからいずれかのx87 FPUデータレジスタにロードされた場合、値は となり、倍精度浮動小数点形式に自動的に変換されます( はまだその形式ではありません)。
FPUレジスタは80ビット幅の浮動小数点数であるため、 - 32ビットintは完全に浮動小数点形式の64ビットの仮数に収まるようfild
とはここでの問題はありません。
これまでのところとても良いです。
2番目の部分 - fstp
は少しトリッキーで、驚くかもしれません。これは、32bit浮動小数点で80bit浮動小数点を格納することになっています。それは整数値に関するものですが(質問の)コプロセッサは実際には「丸め」を実行することがあります。ケ?浮動小数点形式で格納されている場合でも、整数値はどのように丸めますか? ;-)。
私はまもなく説明します。最初にx87が提供する丸めモード(IEE 754丸めモードのインカネーション)を見ていきましょう。 X87 fpuには、fpuの制御ワードのビット#10と#11によって制御される4つの丸めモードがあります。
- 00 - 最も近い偶数に丸められた結果は無限に正確です。 2つの値が等しく近い場合、結果は偶数の値になります(つまり、最下位ビットがゼロの )。 デフォルト
- 01 - + INF
- 11に向けて - - -Inf
- 10側に向けて0
あなたはこの単純なコードを使用して丸めモードで遊ぶことができます(それものの(すなわち切り捨てます。)ここでは低レベルを示しています):
enum ROUNDING_MODE
{
RM_TO_NEAREST = 0x00,
RM_TOWARD_MINF = 0x01,
RM_TOWARD_PINF = 0x02,
RM_TOWARD_ZERO = 0x03 // TRUNCATE
};
void set_round_mode(enum ROUNDING_MODE rm)
{
short csw;
short tmp = rm;
_asm
{
push ax
fstcw [csw]
mov ax, [csw]
and ax, ~(3<<10)
shl [tmp], 10
or ax, tmp
mov [csw], ax
fldcw [csw]
pop ax
}
}
これは整数値にどのように関連していますか?忍耐...切り捨て(デフォルトではない) - - あなたが浮かぶようにint型に変換する変換チェック最も明白な方法をfloat型にint型に関与丸めモードが必要になる場合があります理由を理解するには、次のようになります。
- レコード記号
- あなたのint型を否定左/右の左端の1
- シフトintの場合、ゼロ未満
- 検索位置あなたが指数
を計算することができるように上記見つかっ1は、プロセス中のシフトのビット#23
- レコード番号の上に位置するように、
そして、このbahaviorをシミュレートするコードは次のようになります。
float int2float(int value)
{
// handles all values from [-2^24...2^24]
// outside this range only some integers may be represented exactly
// this method will use truncation 'rounding mode' during conversion
// we can safely reinterpret it as 0.0
if (value == 0) return 0.0;
if (value == (1U<<31)) // ie -2^31
{
// -(-2^31) = -2^31 so we'll not be able to handle it below - use const
value = 0xCF000000;
return *((float*)&value);
}
int sign = 0;
// handle negative values
if (value < 0)
{
sign = 1U << 31;
value = -value;
}
// although right shift of signed is undefined - all compilers (that I know) do
// arithmetic shift (copies sign into MSB) is what I prefer here
// hence using unsigned abs_value_copy for shift
unsigned int abs_value_copy = value;
// find leading one
int bit_num = 31;
int shift_count = 0;
for(; bit_num > 0; bit_num--)
{
if (abs_value_copy & (1U<<bit_num))
{
if (bit_num >= 23)
{
// need to shift right
shift_count = bit_num - 23;
abs_value_copy >>= shift_count;
}
else
{
// need to shift left
shift_count = 23 - bit_num;
abs_value_copy <<= shift_count;
}
break;
}
}
// exponent is biased by 127
int exp = bit_num + 127;
// clear leading 1 (bit #23) (it will implicitly be there but not stored)
int coeff = abs_value_copy & ~(1<<23);
// move exp to the right place
exp <<= 23;
int ret = sign | exp | coeff;
return *((float*)&ret);
}
今の例 - 切り捨てモードは2147483520
に2147483583
に変換します。
2147483583 = 01111111_11111111_11111111_10111111
int-> float変換では、左端の1をビット23にシフトする必要があります。今や1をリードするのがビット#30です。ビット#23に配置するには、7つの位置で右シフトを実行する必要があります。その間にあなたは緩んでいます(32ビットの浮動小数点フォーマットに収まらないでしょう)。右から7 lsbビット(切り捨て/切り詰める)。彼らは次のとおりだった:
01111111 = 63
そして63は元の数が失わです:
2147483583 -> 2147483520 + 63
切捨ては簡単ですが、必ずしもあなたが望むものではないかもしれない、および/またはすべてのケースのために最善をです。例の下に考えてみましょう:値を上回る
67108871 = 00000100_00000000_00000000_00000111
は正確にフロートによって表されるが、それには何を切り捨てをチェックすることはできません。これまでのように、左端の1をビット#23に移動する必要があります。
00000001.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
切り捨てチョップ:これは3 LSBビット(今のように私は違ったフロートの暗黙の24ビットであり、仮の明示的な23bitsを挟むれる場所を示す数字を書きます)を失う右正確に3つの位置をシフトさせる値を必要と67108864
(67108864 + 7(3チョップビット))= 67108871(私たちはシフトしますが、指数操作で補正しますが、ここでは省略します)を残す3つの末尾のビット。
これで十分ですか?ちょっと67108872
は32bit floatで完全に表現でき、67108864
よりはるかに良いはずですか?これはintを32bit floatに変換するときの丸めについて話したいと思うかもしれません。
ここで、デフォルトの「最も近い偶数」モードへの丸めがどのように機能し、OPの場合にどのような意味があるかを見てみましょう。同じ例をもう一度考えてみましょう。
67108871 = 00000100_00000000_00000000_00000111
我々は右ビット#23で1左端を置くに移行3を必要と知っているように:「でも、最寄りに丸める」の
00000000_1.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
手順は下からそのブラケット入力値67108871
を2つの数字を見つける必要可能な限り近くに置いてください。私たちはまだ80ビットでFPU内で動作しているので、いくつかのビットがシフトアウトされていることを示していますが、まだFPUレジスタにありますが、出力値を格納するときは丸め処理中に削除されます。
00000000_1.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
2密接ブラケット00000000_1.[0000000_00000000_00000000] 111 * 2^26
がある値:上から
:
00000000_1.[0000000_00000000_00000000] 111 * 2^26
+1
= 00000000_1.[0000000_00000000_00000001] * 2^26 = 67108872
以下から:明らか
00000000_1.[0000000_00000000_00000000] * 2^26 = 67108864
67108872
が故に67108864
より67108871
にはるかに近いです32bit int値からの変換67108871
は、67108872
(最も近い偶数モードに丸める)を示します。
今OPの番号(まださえ最寄りに丸める):
2147483583 = 01111111_11111111_11111111_10111111
= 00000000_1.[1111111_11111111_11111111] 0111111 * 2^30
ブラケット値:
トップ:
00000000_1.[1111111_111111111_11111111] 0111111 * 2^30
+1
= 00000000_10.[0000000_00000000_00000000] * 2^30
= 00000000_1.[0000000_00000000_00000000] * 2^31 = 2147483648
下:
00000000_1.[1111111_111111111_11111111] * 2^30 = 2147483520
は覚えておいてくださいそのでも入力値が括弧の値の中間にある場合にのみ、「最も近い偶数に丸める」問題の単語。その後、という単語だけがであり、どのブラケット値を選択すべきかを決定する。上記の場合でさえは問題ではないと、私たちは単純に近い値を選択する必要があり、ある2147483520
最終OPの場合は、でもワード事項の問題を示しています。 :
2147483584 = 01111111_11111111_11111111_11000000
= 00000000_1.[1111111_11111111_11111111] 1000000 * 2^30
ブラケット値が以前と同じである。
トップ:00000000_1.[0000000_00000000_00000000] * 2^31 = 2147483648
底:00000000_1.[1111111_111111111_11111111] * 2^30 = 2147483520
全く近い値が(2147483648から2147483584 = 64 = 2147483584から2147483520今はありません)でもに依存し、トップ(偶数)の値2147483648
を選択する必要があります。
ここでOPの問題は、パスカルが簡単に説明したことです。 FPUは符号付きの値に対してのみ機能し、2147483648
は最大値が2147483647なので符号付き整数として格納できません。
FPUは符号付きの値に対してのみ動作することを簡単に証明します。扱い署名されたように、すべての値は、このデバッグすることによるものである。
unsigned int test = (1u << 31);
_asm
{
fild [test]
}
試験値がローディング付きおよび符号なしの値のための別の指示がないように、それはのよう-2 ロードされる符号なしとして扱われるべきであるように見えるがFPUに書き込む。同様に、FPUの符号なしの値をmemに格納するための命令を見つけることもできません。あなたのプログラムでどのように宣言したのかにかかわらず、すべてが署名付きで扱われるちょっとしたパターンです。
長いですが、誰かがそれから何かを学ぶことを願っています。
x86_64で '' g ++(GCC)4.8.2 20131017(Red Hat 4.8.2-1) ''で記述した動作(ideoneのものではない)を確認できます。 –
@Mat:これを確認できますが、 '' -O {s、1,2,3} ''のどれであるかは関係ありません。 –
数字は仮数部には大きすぎますか? –