9

コンパイル時 (または代わりに実行時) に 'float'、'double'、および 'long double' の構造を決定する C++ 標準に準拠した方法はありますか?

と を仮定するstd::numeric_limits< T >::is_iec559 == truestd::numeric_limits< T >::radix == 2、次のルールによって が可能であると思われます。

  • 最初の X ビットは仮数です。
  • 次の Y ビットは指数です。
  • 最後の 1 ビットは符号ビットです。

次の表現が漠然と好きです:

  • size_t num_significand_bits = std::numeric_limits< T >::digits;
  • size_t num_exponent_bits = log2( 2 * std::numeric_limits< T >::max_exponent );
  • size_t num_sign_bits = 1u;

私が知っている以外は

  • std::numeric_limits< T >::digitsフォーマットが実際にそれを明示的に表しているかどうかにかかわらず、「整数ビット」が含まれているため、プログラムでこれを検出して調整する方法がわかりません。
  • std::numeric_limits< T >::max_exponentはいつもだと思い2^(num_exponent_bits)/2ます。

背景:私は移植可能な2つの問題を克服しようとしています:

  • 仮数部にあるビットを設定/取得します。
  • 「long double」の終わりがどこにあるかを判断するので、初期化されていないメモリを持つ暗黙的なパディングビットを読み取らないことがわかります。
4

3 に答える 3

5

要するに、違います。の場合、多かれ少なかれ、std::numeric_limits<T>::is_iec559の形式を知っています。それでもバイト順序を決定する必要があります。Tそれ以外の場合は、すべての賭けがオフになっています。(まだ使用されていることがわかっている他の形式は、基数2でもありません。たとえば、IBMメインフレームは基数16を使用します。)IEC浮動小数点の「標準」配置では、上位ビットに符号があり、次に指数があります。 、および下位ビットの仮数。uint64_tたとえば、(memcpyreinterpret_castまたは union—` memcpyを使用して動作することが保証されていますが、他の2つよりも効率が低い)として正常に表示できる場合は 、次のようになります。

のためにdouble

uint64_t tmp;
memcpy( &tmp, &theDouble, sizeof( double ) );
bool isNeg = (tmp & 0x8000000000000000) != 0;
int  exp   = (int)( (tmp & 0x7FF0000000000000) >> 52 ) - 1022 - 53;
long mant  = (tmp & 0x000FFFFFFFFFFFFF) | 0x0010000000000000;

`floatの場合:

uint32_t tmp;
memcpy( &tmp, &theFloat, sizeof( float ) );
bool isNeg = (tmp & 0x80000000) != 0;
int  exp   = (int)( (tmp & 0x7F800000) >> 23 ) - 126 - 24 );
long mant  = (tmp & 0x007FFFFF) | 0x00800000;

に関してはlong double、同じマシン上であっても、コンパイラが異なれば処理も異なるため、さらに悪いことになります。名目上は10バイトですが、配置上の理由から、実際には12バイトまたは16バイトになる場合があります。または単に。の同義語ですdouble。 10バイトを超える場合は、最初の10バイトにパックされていることを期待できると思い&myLongDoubleます。これにより、10バイトの値のアドレスが得られます。しかし、一般的に言えば、私は避けたいと思い long doubleます。

于 2013-03-08T19:14:28.627 に答える
1

移植可能な唯一の方法は、数値を文字列として保存することだと思います。これは「ビットパターンの解釈」に依存していません

何かが何ビットであるかを知っていても、それが同じ表現、つまり指数がゼロベースまたはバイアスされていることを意味するわけではありません。仮数の前に見えない 1 がありますか? 同じことが、数字の他のすべての部分にも当てはまります。そして、BCD エンコードまたは「16 進」浮動小数点の場合はさらに悪化します。これらは一部のアーキテクチャで利用できます...

構造体 (クラス、配列など) の初期化されていないビットが心配な場合は、memset を使用して構造体全体をゼロ [またはその他の既知の値] に設定します。

于 2013-03-08T18:55:44.250 に答える
0

後世のために、これは私がやったことです。

IEEE-754 シグナリング NaN 値を生成してテストするには、このパターンを「float」と「double」に使用します。

#include <cstdint> // uint32_t, uint64_t
#include <limits> // numeric_limits

union IEEE754_Float_Union
{
    float value;
    uint32_t bits;
};

float generate_IEEE754_float()
{
    IEEE754_Float_Union u = { -std::numeric_limits< float >::signaling_NaN() };
    size_t const num_significand_bits_to_set = std::numeric_limits< float >::digits
                                               - 1 // implicit "integer-bit"
                                               - 1; // the "signaling-bit"
    u.bits |= ( static_cast< uint32_t >( 1 ) << num_significand_bits_to_set ) - 1;
    return u.value;
}

bool test_IEEE754_float( float const& a_r_val )
{
    IEEE754_Float_Union const u = { a_r_val };
    IEEE754_Float_Union const expected_u = { generate_IEEE754_float() };
    return u.bits == expected_u.bits;
}

「long double」については、キャストで「double」関数を使用します。具体的には、「double」値を生成し、それが返される前に「long double」にキャストし、「double」にキャストしてからその値をテストすることで「long double」をテストします。私の考えでは、'long double' の形式はさまざまですが、'double' を 'long double' にキャストし、後でそれを 'double' にキャストすることは一貫している必要があります (つまり、情報を失うことはありません)。

于 2013-03-11T17:35:32.917 に答える