5

編集:仮数を正規化する際に、最初に暗黙のビットを設定することが重要です。暗黙のビットをデコードするときは、追加する必要はありません。そこにある情報が本当に役に立ったので、マークされた答えを正しいままにしました。

現在、エンコーディング (識別エンコーディング規則) を実装していますが、double 値のエンコーディングに若干の問題があります。

したがって、次を使用して、C# の double から符号、指数、および仮数を取得できます。

 // get parts
 double value = 10.0;
 long bits = BitConverter.DoubleToInt64Bits(value);
 // Note that the shift is sign-extended, hence the test against -1 not 1
 bool negative = (bits < 0);
 int exponent = (int)((bits >> 52) & 0x7ffL);
 long mantissa = bits & 0xfffffffffffffL;

( hereのコードを使用)。これらの値はエンコードすることができ、プロセスを逆にするだけで元の double が返されます。

ただし、DER エンコーディング規則では、仮数を正規化する必要があると指定されています。

Canonical Encoding Rules と Distinguished Encoding Rules では正規化が指定されており、最下位ビットが 1 になるまで仮数 (0 でない限り) を繰り返しシフトする必要があります。

(セクション 8.5.6.5 のこちらを参照してください)。

以下を使用して手動でこれを行います。

 while ((mantissa & 1) == 0)
 {
     mantissa >>= 1;
     exponent++;
 }

動作せず、奇妙な値が返されます。(前述のリンクに投稿されたJon Skeetの機能全体を使用する場合でも)。

ここで何かが欠けているようです。最初に double の仮数を正規化し、「ビット」を取得できれば最も簡単です。ただし、手作業による正規化が正しく機能しない理由もよくわかりません。

助けてくれてありがとう、

ダニー

編集:カマキリの正規化に関する私の問題を示す実際の作業上の問題:

 static void Main(string[] args)
    {
        Console.WriteLine(CalculateDouble(GetBits(55.5, false))); 
        Console.WriteLine(CalculateDouble(GetBits(55.5, true)));
        Console.ReadLine();
    }

    private static double CalculateDouble(Tuple<bool, int, long> bits)
    {
        double result = 0;
        bool isNegative = bits.Item1;
        int exponent = bits.Item2;
        long significand = bits.Item3;

        if (exponent == 2047 && significand != 0)
        {
            // special case
        }
        else if (exponent == 2047 && significand == 0)
        {
            result = isNegative ? double.NegativeInfinity : double.PositiveInfinity;
        }
        else if (exponent == 0)
        {
            // special case, subnormal numbers
        }
        else
        {
            /* old code, wont work double actualSignificand = significand*Math.Pow(2,                   
               -52) + 1; */
            double actualSignificand = significand*Math.Pow(2, -52);
            int actualExponent = exponent - 1023;
            if (isNegative)
            {
                result = actualSignificand*Math.Pow(2, actualExponent);
            }
            else 
            {
                result = -actualSignificand*Math.Pow(2, actualExponent);**strong text**
            }
        }
        return result;

    }


    private static Tuple<bool, int, long> GetBits(double d, bool normalizeSignificand)
    {
        // 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 < 0);
        int exponent = (int)((bits >> 52) & 0x7ffL);
        long significand = bits & 0xfffffffffffffL;

        if (significand == 0)
        {
            return Tuple.Create<bool, int, long>(false, 0, 0);
        }
        // fix: add implicit bit before normalization
        if (exponent != 0)
        {
            significand = significand | (1L << 52);
        }
        if (normalizeSignificand)
        {
            //* Normalize */
            while ((significand & 1) == 0)
            {
                /*  i.e., Mantissa is even */
                significand >>= 1;
                exponent++;
            }
        }
        return Tuple.Create(negative, exponent, significand);

    }
    Output:
    55.5
    2.25179981368527E+15
4

1 に答える 1

3

を使用すると、すでに IEEE 754 形式でエンコードされBitConverter.DoubleToInt64Bitsた値が得られます。doubleこれは、仮数が暗黙の先行ビットでエンコードされることを意味します。(「仮数」は、浮動小数点値の小数部の優先用語であり、IEEE 754 で使用されます。仮数は線形です。仮数は対数です。「仮数」は、人々が対数と紙を使用しなければならなかった時代に由来します。大雑把な計算を行うための関数の表。) エンコードされていない有意桁を復元するには、暗黙のビットを復元する必要があります。

それは難しいことではありません。符号ビット、エンコードされた指数 (整数として)、およびエンコードされた仮数 (整数として) を分離したら、64 ビット 2 進浮動小数点の場合:

  • エンコードされた指数が最大値 (2047) で、エンコードされた仮数がゼロ以外の場合、値は NaN です。NaN がシグナリングしているかどうか、およびその他のユーザー定義または実装定義の情報に関する追加情報が有意桁に含まれています。
  • エンコードされた指数が最大値で、エンコードされた仮数がゼロの場合、値は無限大 (符号に応じて + または –) になります。
  • エンコードされた指数がゼロの場合、暗黙のビットはゼロであり、実際の仮数はエンコードされた仮数に 2 –52を掛けたものであり、実際の指数は 1 からバイアス (1023) を引いたもの (つまり –1022) です。
  • それ以外の場合、暗黙のビットは 1 であり、実際の仮数はエンコードされた仮数に最初に 2 –52を掛けてから 1 を加えたものであり、実際の指数はエンコードされた指数からバイアス (1023) を引いたものです。

(整数で作業したいが、仮数に分数がない場合は、2 –52による乗算を省略し、代わりに指数に –52 を追加できます。後者の場合、仮数は1 ではなく2 52に追加されます。 .)

BitConverterIEEE-754 エンコーディングを回避する別の方法があります。C# からこのルーチンを呼び出すことができる場合はfrexp、分数と指数がエンコーディングではなく数学的に返されます。まず、ゼロ、無限大、および NaN を個別に処理します。次に使用します。

int exponent;
double fraction = frexp(value, &exponent);

これはfraction、大きさが [½, 1) でexponentfraction•2exponentが に等しい値に設定されますvalue。(まだ符号があることに注意してくださいfraction。それを分離して絶対値を使用することをお勧めします。)

この時点で、必要に応じてスケーリングできfractionます (それに応じて調整できますexponent)。奇数になるようにスケーリングするには、小数部がなくなるまで繰り返し 2 を掛けます。

于 2013-07-24T13:58:23.843 に答える