尾数归一化C#双 [英] Mantissa Normalization of C# double

查看:236
本文介绍了尾数归一化C#双的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:现在可以正常工作,在对螳螂进行规范化时,首先设置隐式位是很重要的,当解码隐式位时,不必添加。
我将标记的答案留给正确,正如有关信息真的有帮助。



我正在执行一个编码(区别编码规则)问题编码双重值。



所以,我可以通过使用以下方式,从c#中的double中取出符号,指数和尾数:

  //获取零件
double value = 10.0;
long bits = BitConverter.DoubleToInt64Bits(value);
//注意,移位是符号扩展的,因此对-1的测试不是1
bool negative =(bits< 0);
int exponent =(int)((bits>>> 52)& 0x7ffL);
long mantissa = bits& 0xfffffffffffffL;

(使用此处)。
这些值可以被编码,简单的逆转将使我回到原来的双倍。



但是,DER编码规则规定尾数应该要归一化:



规范编码规则和可分辨编码规则规范化中指定尾数(除非为0) )需要
重复移动,直到最低有效位为1。


(请参阅在8.5.6.5节)。



手工使用:

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

不行,给我奇怪的价值。 (即使使用整个函数Jon Skeet发布在上述链接中)



我似乎在这里缺少一些东西,如果我第一次可以归一化mantiassa,这将是最简单的的双倍,并获得位。但是,我也不太明白为何手工正常化将无法正常工作。



感谢任何帮助,



Danny



编辑:显示我的问题与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有效位数= bits.Item3;

如果(exponent == 2047&&有效值= 0)
{
//特殊情况
}
else if(exponent = = 2047&&有效值== 0)
{
result = isNegative? double.NegativeInfinity:double.PositiveInfinity;
}
else if(exponent == 0)
{
//特殊情况,次正常数
}
else
{
/ *旧代码,不工作double actualSignificand =有效数* Math.Pow(2,
-52)+ 1; * /
double actualSignificand =有效数* Math.Pow(2,-52);
int actualExponent = exponent - 1023;
if(isNegative)
{
result = actualSignificand * Math.Pow(2,actualExponent);
}
else
{
result = -actualSignificand * Math.Pow(2,actualExponent); **强文本**
}
}
返回结果;

}


private static Tuple< bool,int,long> GetBits(double d,bool normalizeSignificand)
{
//将double转换为符号,指数和尾数。
long bit = BitConverter.DoubleToInt64Bits(d);
//注意,移位是符号扩展的,因此对-1的测试不是1
bool negative =(bits< 0);
int exponent =(int)((bits>>> 52)& 0x7ffL);
long有效位数=位& 0xfffffffffffffL;

如果(有效值为0)
{
返回Tuple.Create< bool,int,long>(false,0,0);
}
//修复:在规范化之前添加隐式位
if(exponent!= 0)
{
significantand = significantand | (1L << 52);
}
if(normalizeSignificand)
{
// * Normalize * /
while((significantand& 1)== 0)
{
/ * ie,尾数是* /
有效值>> = 1;
exponent ++;
}
}
return Tuple.Create(negative,exponent,significantand);

}
输出:
55.5
2.25179981368527E + 15


解决方案

当您使用 BitConverter.DoubleToInt64Bits 时,它会为您提供 double 已经以IEEE 754格式编码。这意味着有效位数用隐式前导位编码。 (Significand是浮点值分数部分的首选项,在IEEE 754中使用。有效数为线性,尾数为对数,尾数源于人们不得不使用对数和纸张的日子和表的功能做粗略计算。)要恢复未编码的有效位数,您将不得不恢复隐式位。



这不难。一旦将符号位,编码指数(作为整数)和编码的有效数(作为整数)分离,则为64位二进制浮点数:




  • 如果编码指数是其最大值(2047),编码的有效位数不为零,则该值为NaN。有关NaN是否发信号的其他信息和其他用户或实现定义的信息。

  • 如果编码指数是其最大值,编码有效位数为零,值为无穷大(+或 - 根据符号)

  • 如果编码指数为零,则隐含位为零,实际有效位数乘以2 < sup> -52 ,实际指数为减去偏差(1023)(所以-1022)。

  • 否则,隐含位为1,实际有效位数为编码有效位数首先乘以2 -52 ,然后添加到一个,实际指数是编码指数减去偏差(1023)。



(如果要使用整数,而不是有效数的分数,则可以通过2 -52 省略乘法,并将-52添加到指数在最后一种情况下,有效数被添加到2 52 而不是一个。)



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

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

这将分数设置为一个数值在[½,1)和指数中,使分数•2 exponent 等于。 (请注意,分数仍然有符号;您可能需要分开并使用绝对值。)



此时,您可以根据需要缩放 fraction (并相应地调整 exponent )。要缩放它,以便它是一个奇整数,你可以重复乘以2,直到它没有小数部分。


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. 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:

 // 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:

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.

(see here in section 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).

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". 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:

 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. 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.

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:

  • If the encoded exponent is its maximum (2047) and the encoded significand is non-zero, the value is a NaN. There is additional information in the significand about whether the NaN is signaling or not and other user- or implementation-defined information.
  • 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).
  • 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).

(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 252 instead of to one.)

There is an alternative method that avoids BitConverter and the IEEE-754 encoding. If you can call the frexp routine from C#, it will return the fraction and exponent mathematically instead of as encodings. First, handle zeroes, infinities, and NaNs separately. Then use:

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

This sets fraction to a value with magnitude in [½, 1) and exponent such that fraction•2exponent equals value. (Note that fraction still has the sign; you might want to separate that and use the absolute value.)

At this point, you can scale fraction as desired (and adjust exponent accordingly). To scale it so that it is an odd integer, you could multiply it by two repeatedly until it has no fractional part.

这篇关于尾数归一化C#双的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆