0

倍精度 IEEE 浮動小数点値を 2 つの整数に分解し、後でそれらを完全な忠実度で再構成できるかどうかを識別しようとしています。次のようなものを想像してください。

double foo = <inputValue>;
double ipart = 0;
double fpart = modf(foo, &ipart);

int64_t intIPart = ipart;
int64_t intFPart = fpart * <someConstant>;

double bar = ((double)ipart) + ((double)intFPart) / <someConstant>;

assert(foo == bar);

任意の 64 ビット量を 128 ビットで格納できることは論理的に明らかです (つまり、リテラル ビットを格納するだけです)。ここでの目標は、倍精度浮動小数点数の整数部分と小数部分を整数表現に分解することです (および API 2 つの 64 ビット整数を再構成すると、ビット単位で正確な double が返されます。

私は IEEE 浮動小数点の概念を理解しており、double は基数 2 で格納されることがわかりました。経験的に、上記のアプローチでは、 のfoo != bar値が非常に大きい場合さえあることがわかります<someConstant>。私はしばらく学校を休んでいましたが、これが可能かどうかを理解するためのループを完全に閉じることはできません。

編集:

これは私の脳内で暗示/理解されていたと思いますが、ここではキャプチャされませんでした: この状況では、質問の double の全体的な大きさが常に +/- 2^63 (および > 2^-64) 以内になることが保証されます。 . その理解により、整数部分は 64 ビットの int 型に収まることが保証されます。私の予想では、10 進数の精度が 16 ビットまでであれば、小数部分も 64 ビットの int 型で簡単に表現できるはずです。

4

3 に答える 3

5

数値が [–2 63 , +2 63 ) 内にあり、ULP (数値の最下位ビットの値) が少なくとも 2 -63であることがわかっている場合、これを使用できます。

double ipart;
double fpart = modf(foo, &ipart);

int64_t intIPart = ipart;
int64_t intFPart = fpart * 0x1p63;

double bar = intIPart + intFPart * 0x1p-63;

値を再構築できるいくつかの整数だけが必要で、それらの整数の意味を気にしない場合 (たとえば、そのうちの 1 つが整数部分である必要はありません)、frexp数値を逆アセンブルするために使用できます。その仮数(符号付き)と指数に変換し、ldexpそれを再構築するために使用できます:

int exp;
int64_t I = frexp(foo, &exp) * 0x1p53;
int64_t E = exp;

double bar = ldexp(I, E-53);

このコードは、IEEE-754 64 ビット 2 進浮動小数点オブジェクトの任意の有限値に対して機能します。無限大または NaN はサポートされていません。

I面倒なことをしたい場合は、 andEを単一の int64_t にパックすることもできます。

于 2013-05-14T13:40:28.763 に答える
1

ここでの目標は、double の整数部分と小数部分を整数表現に分解することです。

整数部分だけ、または小数部分だけを確実に取得することさえできません。問題は、浮動小数点数の格納方法を誤解しているように見えることです。整数部分と小数部分はありません。それらには、仮数と呼ばれる有効数字部分と指数があります。指数は基本的に、科学表記法と同様に、仮数を増減します。

倍精度浮動小数点数の指数は 11 ビットで、 2 -1022 ...2 1023のような値の範囲になります。整数部分と小数部分を格納する場合は、それぞれが約 2 10ビットの 2 つの整数が必要になります。ただし、それはばかげた方法です。仮数のビットのみが重要であるため、これらのビットのほとんどは使用されません。2 つの非常に長い整数を使用すると、double の範囲全体のすべての値をどこでも同じ精度で表すことができます。これは、double ではできないことです。たとえば、非常に小さな小数部分を持つ非常に大きな整数部分を持つことができますが、それは double では正確に表現できない数値です。

アップデート

コメントで示したように、問題の値が ±2 63の範囲内にあることがわかっている場合は、次のように、C で double の小数部分を *効率的に* 抽出するための回答を使用できます。

double whole = // your original value
long iPart = (long)whole;
double fraction = whole - iPart;
long fPart = fraction * (2 << 63);

私はそれをテストしていませんが、あなたが望むものを手に入れるはずです。

于 2013-05-14T13:24:13.340 に答える
0

double の形式については、ウィキペディアを参照してください。

http://en.wikipedia.org/wiki/Double-precision_floating-point_format

IEEE double 形式は、仮数、指数、および符号ビットの 3 つの整数をエンコードします。以下は、3 つの構成整数を IEEE double 形式で抽出するコードです。

double d = 2.0;  

// sign bit
bool s = (*reinterpret_cast<int64_t*>(&d)) >> 63;

// significand
int64_t m = *reinterpret_cast<int64_t*>(&d) & 0x000FFFFFFFFFFFFFULL;

// exponent
int64_t e = ((*reinterpret_cast<int64_t*>(&d) >> 52) & 0x00000000000007FFULL) - 1023;

// now the double d is exactly equal to s * (1 + (m / 2^52)) * 2^e
// print out the exact arithmatic expression for d:

std::cout << "d = " << std::dec << (s ? "-(1 + " : "(1 + (") << m << "/" << (1ULL << 52) << ")) x 2^" << e;
于 2013-05-14T13:36:13.820 に答える