web-dev-qa-db-ja.com

C#のdoubleから仮数と指数を抽出する

C#(または一般的には.NET)のdoubleから仮数と指数を取得する簡単な方法はありますか?

私は見つけました この例 グーグルを使用していますが、それがどれほど堅牢かはわかりません。フレームワークの将来のバージョンなどで、二重のバイナリ表現を変更できますか?

私が見つけた他の代替手段は、doubleの代わりにSystem.Decimalを使用し、 Decimal.GetBits() メソッドを使用してそれらを抽出することでした。

助言がありますか?

26
dvenema

バイナリ形式は変更すべきではありません-それは確かに既存の仕様への重大な変更になるでしょう。ジミーが言ったように、IEEE754/IEC 60559:1989形式であると定義されています。(C#3.0言語仕様セクション1.3; ECMA 335セクション8.2.2)。DoubleConverterのコードは問題なく、壮健。

後で参照できるように、例のコードの関連ビットは次のとおりです。

public static string ToExactString (double d)
{
    …

    // Translate the double into sign, exponent and mantissa.
    long bits = BitConverter.DoubleToInt64Bits(d);
    // Note that the shift is sign-extended, hence the test against -1 not 1
    bool negative = (bits & (1L << 63)) != 0;
    int exponent = (int) ((bits >> 52) & 0x7ffL);
    long mantissa = bits & 0xfffffffffffffL;

    // Subnormal numbers; exponent is effectively one higher,
    // but there's no extra normalisation bit in the mantissa
    if (exponent==0)
    {
        exponent++;
    }
    // Normal numbers; leave exponent as it is but add extra
    // bit to the front of the mantissa
    else
    {
        mantissa = mantissa | (1L << 52);
    }

    // Bias the exponent. It's actually biased by 1023, but we're
    // treating the mantissa as m.0 rather than 0.m, so we need
    // to subtract another 52 from it.
    exponent -= 1075;

    if (mantissa == 0) 
    {
        return negative ? "-0" : "0";
    }

    /* Normalize */
    while((mantissa & 1) == 0) 
    {    /*  i.e., Mantissa is even */
        mantissa >>= 1;
        exponent++;
    }

    …
}

当時のコメントは理にかなっていたのですが、今はしばらく考えないといけないと思います。最初の部分の後に、「生の」指数と仮数があります。残りのコードは、それらをより単純な方法で処理するのに役立ちます。

28
Jon Skeet

表現はIEEE標準であり、変更しないでください。

https://msdn.Microsoft.com/en-us/library/system.double(v = vs.110).aspx

Doubleタイプは、バイナリ浮動小数点演算のIEC 60559:1989(IEEE 754)標準に準拠しています。

編集:decimalにgetBitsがあり、doubleにない理由は、decimalが有効数字を保持するためです。 3.0000m == 3.00mですが、指数/仮数は実際には異なります。フロート/ダブルはユニークに表現されていると思います。

1
Jimmy