尾数标准化C#double

Dan*_*iel 5 c# floating-point encoding

编辑:现在让它工作,同时规范化mantiss,首先设置隐含位是很重要的,当解码隐含位然后不必添加.我将标记的答案保留为正确,因为那里的信息确实有帮助.

我目前正在实现编码(专有编码规则),并且编码双值时会有轻微问题.

所以,我可以通过使用以下方式从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;
Run Code Online (Sandbox Code Playgroud)

(使用此处的代码).这些值可以编码,过程的简单反转将使我恢复原来的双倍.

但是,DER编码规则指定应该对尾数进行规范化:

在规范编码规则和可分辨编码规则中,规定了归一化,并且尾数(除非它是0)需要重复移位,直到最低有效位为1.

(见8.5.6.5节).

手动执行此操作:

 while ((mantissa & 1) == 0)
 {
     mantissa >>= 1;
     exponent++;
 }
Run Code Online (Sandbox Code Playgroud)

不行,并给我奇怪的价值观.(即使使用Jon Skeet在上述链接中发布的整个功能).

我似乎在这里遗漏了一些东西,如果我第一次能够规范化双重的mantiassa并得到"位",那将是最简单的.但是,我也无法真正理解为什么手动标准化将无法正常工作.

谢谢你的帮助,

丹尼

编辑:实际工作问题显示我的mantiss规范化问题:

 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
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 3

当您使用 时BitConverter.DoubleToInt64Bits,它会为您提供double已经以 IEEE 754 格式编码的值。这意味着尾数是用隐式前导位编码的。(\xe2\x80\x9cSignificand\xe2\x80\x9d 是浮点值小数部分的首选术语,用于 IEEE 754。有效数是线性的。尾数是对数的。\xe2\x80\x9cMantissa \xe2\x80\x9d 源于人们不得不使用对数、纸张和函数表进行粗略计算的时代。)要恢复未编码的有效数,您必须恢复隐式位。

\n\n

那并不难。将符号位、编码指数(作为整数)和编码有效数(作为整数)分开后,对于 64 位二进制浮点数:

\n\n
    \n
  • 如果编码的指数为其最大值 (2047) 并且编码的尾数非零,则该值为 NaN。有效数中有关于 NaN 是否是信令的附加信息以及其他用户或实现定义的信息。
  • \n
  • 如果编码的指数是其最大值并且编码的尾数为零,则该值为无穷大(根据符号+或\xe2\x80\x93)。
  • \n
  • 如果编码指数为零,则隐式位为零,实际有效数为编码有效数乘以 2 \xe2\x80\x9352,实际指数为 1 减去偏差 (1023) (因此 \xe2\x80\x931022 )。
  • \n
  • 否则,隐式位为 1,实际有效数为编码后有效数先乘以 2 \xe2\x80\x9352,然后加一,实际指数为编码指数减去偏差 (1023)。
  • \n
\n\n

(如果您想使用整数并且尾数没有分数,则可以省略乘以 2 \xe2\x80\x9352并将 \xe2\x80\x9352 添加到指数。在最后一种情况下,尾数为添加到 2 52而不是 1。)

\n\n

有一种替代方法可以避免BitConverterIEEE-754 编码。如果您可以从 C# 调用该frexp例程,它将以数学方式返回分数和指数,而不是作为编码。首先,分别处理零、无穷大和 NaN。然后使用:

\n\n
int exponent;\ndouble fraction = frexp(value, &exponent);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这设置fraction为一个大小为 [\xc2\xbd, 1) 的值,exponent使得fraction\xe2\x80\xa22exponent等于value。(请注意,fraction仍然有符号;您可能需要将其分开并使用绝对值。)

\n\n

此时,您可以fraction根据需要进行缩放(并进行exponent相应调整)。要将其缩放为奇数,您可以将其重复乘以二,直到它没有小数部分。

\n