简体   繁体   English

尾数标准化C#double

[英]Mantissa Normalization of C# double

EDIT: Got it to work now, while normalizing the mantiss it is important to first set the implicit bit, when decoding the implicit bit then does not have to be added. 编辑:现在让它工作,同时规范化mantiss,首先设置隐含位是很重要的,当解码隐含位然后不必添加。 I left the marked answer as correct, as the information there really helped. 我将标记的答案保留为正确,因为那里的信息确实有帮助。

I'm currently implementing an encoding (Distinguished encoding rules) and have a slight problem encoding double values. 我目前正在实现编码(专有编码规则),并且编码双值时会有轻微问题。

So, I can get out the sign, exponent and mantissa from a double in c# by using: 所以,我可以通过使用以下方式从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;

(using code from here ). (使用此处的代码)。 These values can be encoded and a simple reversal of the process will get me back the original double. 这些值可以编码,过程的简单反转将使我恢复原来的双倍。

However, the DER encoding rules specify that the mantissa should be normalized: 但是,DER编码规则指定应该对尾数进行规范化:

In the Canonical Encoding Rules and the Distinguished Encoding Rules normalization is specified and the mantissa (unless it is 0) needs to be repeatedly shifted until the least significant bit is a 1. 在规范编码规则和可分辨编码规则中,规定了归一化,并且尾数(除非它是0)需要重复移位,直到最低有效位为1。

(see here in section 8.5.6.5 ). (见8.5.6.5节 )。

Doing this by hand using: 手动执行此操作:

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

will not work, and gives me strange values. 不行,并给我奇怪的价值观。 (Even when using the whole function Jon Skeet posted in the aforementioned link). (即使使用Jon Skeet在上述链接中发布的整个功能)。

I seem to be missing something here, it would be easiest if I first could normalize the mantiassa of the double and the get the "bits". 我似乎在这里遗漏了一些东西,如果我第一次能够规范化双重的mantiassa并得到“位”,那将是最简单的。 However, I also can't really see why the normalization by hand won't work correctly. 但是,我也无法真正理解为什么手动标准化将无法正常工作。

Thanks for any help, 谢谢你的帮助,

Danny 丹尼

EDIT: Actual working problem showing my issue with mantiss normalization: 编辑:实际工作问题显示我的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

When you use BitConverter.DoubleToInt64Bits , it gives you the double value already encoded in IEEE 754 format. 当您使用BitConverter.DoubleToInt64Bits ,它会为您提供已经以IEEE 754格式编码的double BitConverter.DoubleToInt64Bits值。 This means the significand is encoded with an implicit leading bit. 这意味着有效数据用隐式前导位编码。 (“Significand” is the preferred term for the fraction portion of a floating-point value and is used in IEEE 754. A significand is linear. A mantissa is logarithmic. “Mantissa” stems from the days when people had to use logarithms and paper and tables of functions to do crude calculations.) To recover the unencoded significand, you would have to restore the implicit bit. (“有效”是浮点值的小数部分的首选术语,用于IEEE 754.有效数是线性的。尾数是对数的。“尾数”源于人们不得不使用对数和纸的日子。和粗略计算的函数表。)要恢复未编码的有效数,您必须恢复隐含位。

That is not hard. 这并不难。 Once you have separated the sign bit, the encoded exponent (as an integer), and the encoded significand (as an integer), then, for 64-bit binary floating-point: 一旦你将符号位,编码的指数(作为整数)和编码的有效数字(作为整数)分开,那么,对于64位二进制浮点:

  • If the encoded exponent is its maximum (2047) and the encoded significand is non-zero, the value is a NaN. 如果编码的指数是其最大值(2047)并且编码的有效数字是非零,则该值是NaN。 There is additional information in the significand about whether the NaN is signaling or not and other user- or implementation-defined information. 关于NaN是否发信号以及其他用户或实现定义的信息,还有关于有效性的附加信息。
  • If the encoded exponent is its maximum and the encoded significand is zero, the value is an infinity (+ or – according to the sign). 如果编码的指数是其最大值且编码的有效数字为零,则该值为无穷大(+或 - 根据符号)。
  • If the encoded exponent is zero, the implicit bit is zero, the actual significand is the encoded significand multiplied by 2 –52 , and the actual exponent is one minus the bias (1023) (so –1022). 如果编码指数为零,则隐含位为零,实际有效位数为编码有效数,乘以2 -52 ,实际指数为1减去偏差(1023)(所以-1022)。
  • Otherwise, the implicit bit is one, the actual significand is the encoded significand first multiplied by 2 –52 and then added to one, and the actual exponent is the encoded exponent minus the bias (1023). 否则,隐含位是1,实际有效位数是编码的有效位数,首先乘以2 -52然后加到1,实际指数是编码指数减去偏差(1023)。

(If you want to work with integers and not have fractions for the significand, you can omit the multiplications by 2 –52 and add –52 to the exponent instead. In the last case, the significand is added to 2 52 instead of to one.) (如果你想使用整数而不是有效数的分数,你可以省略乘法2 -52并将-52加到指数上。在最后一种情况下,有效数加到2 52而不是1 。)

There is an alternative method that avoids BitConverter and the IEEE-754 encoding. 有一种替代方法可以避免BitConverter和IEEE-754编码。 If you can call the frexp routine from C#, it will return the fraction and exponent mathematically instead of as encodings. 如果你可以从C#调用frexp例程,它将以数学方式返回分数和指数而不是编码。 First, handle zeroes, infinities, and NaNs separately. 首先,分别处理零,无穷大和NaN。 Then use: 然后使用:

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

This sets fraction to a value with magnitude in [½, 1) and exponent such that fraction •2 exponent equals value . 这将fraction设置为幅度为[½,1]且exponent ,使得fraction •2 exponent等于value (Note that fraction still has the sign; you might want to separate that and use the absolute value.) (注意, fraction仍然有符号;您可能希望将其分开并使用绝对值。)

At this point, you can scale fraction as desired (and adjust exponent accordingly). 此时,您可以根据需要缩放fraction (并相应地调整exponent )。 To scale it so that it is an odd integer, you could multiply it by two repeatedly until it has no fractional part. 要缩放它以使其为奇数,您可以将其重复乘以2,直到它没有小数部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM