フロートをbigintに変換します(別名ポータブルな方法でバイナリ指数とマンティッサを取得)

StackOverflow https://stackoverflow.com/questions/2133903

質問

C ++では、任意のサイズの整数を保持できるBigintクラスがあります。

大きなフロートまたはダブル番号をBigintに変換したいと思います。私には実用的な方法がありますが、それはちょっとしたハックです。 IEEE 754番号仕様を使用して、バイナリサイン、マンティッサ、入力番号の指数を取得しました。

ここにコードがあります(ここではサインが無視されます、それは重要ではありません):

 float input = 77e12;
 bigint result;

 // extract sign, exponent and mantissa, 
 // according to IEEE 754 single precision number format
 unsigned int *raw = reinterpret_cast<unsigned int *>(&input); 
 unsigned int sign = *raw >> 31;
 unsigned int exponent = (*raw >> 23) & 0xFF;
 unsigned int mantissa = *raw & 0x7FFFFF;

 // the 24th bit is always 1.
 result = mantissa + 0x800000;

 // use the binary exponent to shift the result left or right
 int shift = (23 - exponent + 127);
 if (shift > 0) result >>= shift; else result <<= -shift;

 cout << input << " " << result << endl;

それは機能しますが、それはかなり醜く、それがどれほどポータブルであるかはわかりません。これを行うためのより良い方法はありますか?フロートまたはダブルからバイナリマンティッサと指数を抽出する醜い、ポータブルな方法はありますか?


答えをありがとう。後世のために、FREXPを使用したソリューションを次に示します。ループのために効率が低下しますが、フロートとダブルの両方で機能したり、reintrepret_castを使用したり、フローティングポイント数の表現の知識に依存しません。

float input = 77e12;
bigint result;

int exponent;
double fraction = frexp (input, &exponent);
result = 0;
exponent--;
for (; exponent > 0; --exponent)
{
    fraction *= 2;
    if (fraction >= 1)
    {
        result += 1;
        fraction -= 1;
    }
    result <<= 1;
}   
役に立ちましたか?

解決

通常、値を抽出できません FREXP()、FREXPF()、FREXPL()?

他のヒント

私はあなたの解決策が好きです!それは私を正しい軌道に乗せました。

私は1つのことをお勧めします - 一度にたくさんのビットを手に入れてみませんか、ほとんど常にループを排除してみませんか?次のようなフロートからビギントへの関数を実装しました。

template<typename F>
explicit inline bigint(F f, typename std::enable_if<(std::is_floating_point<F>::value)>::type* enable = nullptr) {
    int exp;
    F fraction = frexp(fabs(f),&exp);
    F chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
    *this = ulong(chunk); // will never overflow; frexp() is guaranteed < 1
    exp -= ulong_bit_count;
    while (sizeof(F) > sizeof(ulong) && (fraction -= chunk)) // this is very unlikely
    {
        chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
        *this <<= ulong_bit_count;
        (*this).data[0] = ulong(chunk);
        exp -= ulong_bit_count;
    }
    *this <<= exp;
    sign = f < 0;
}

(ちなみに、私はフローティングポイントの2つの定数を入れる簡単な方法がわからないので、FLOAT_POW_2を次のように定義しました):

template<typename F, unsigned Exp, bool Overflow = (Exp >= sizeof(unsigned))>
struct float_pow_2 {
    static constexpr F value = 1u << Exp;
};
template<typename F, unsigned Exp>
struct float_pow_2<F,Exp,true> {
    static constexpr F half = float_pow_2<F,Exp/2>::value;
    static constexpr F value = half * half * (Exp & 1 ? 2 : 1);
};

フロートに常に積分値が含まれている場合は、int:float_to_int =(unsigned long)入力にキャストするだけです。

ところで、77E12フロートをオーバーフローします。ダブルはそれを保持しますが、その後、このキャストが必要です:(署名されていない長い長い)入力。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top