我将标记的答案保留为正确,因为那里的信息确实有帮助.
我目前正在实现编码(专有编码规则),并且编码双值时会有轻微问题.
所以,我可以通过使用以下方式从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的代码).
这些值可以编码,过程的简单反转将使我恢复原来的双倍.
但是,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.
(见here in section 8.5.6.5).
手动执行此 *** 作:
while ((mantissa & 1) == 0) { mantissa >>= 1; exponent++; }
不行,并给我奇怪的价值观. (即使使用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,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,long>(false,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解决方法 当您使用BitConverter.DoubletoInt64Bits时,它会为您提供已经以IEEE 754格式编码的双精度值.这意味着有效数据用隐式前导位编码. (“有效”是浮点值的小数部分的首选术语,用于IEEE 754.有效数是线性的.尾数是对数的.“尾数”源于人们不得不使用对数和纸的日子.和粗略计算的函数表.)要恢复未编码的有效数,您必须恢复隐含位.
这并不难.一旦你将符号位,编码的指数(作为整数)和编码的有效数字(作为整数)分开,那么,对于64位二进制浮点:
>如果编码的指数是其最大值(2047)且编码的有效数字不为零,则该值为NaN.关于NaN是否发信号以及其他用户或实现定义的信息,还有关于有效性的附加信息.
>如果编码的指数是其最大值且编码的有效数为零,则该值为无穷大(或 – 根据符号).
>如果编码指数为零,则隐含位为零,实际有效位数为编码有效数乘以2-52,实际指数为1减去偏差(1023)(所以-1022).
>否则,隐含位为1,实际有效位数是编码的有效位数,首先乘以2-52然后加到1,实际指数是编码指数减去偏差(1023).
(如果你想使用整数而不是有效数的分数,你可以省略乘法2-52并将-52加到指数中.在最后一种情况下,有效数加到252而不是1. )
有一种替代方法可以避免BitConverter和IEEE-754编码.如果你可以从C#调用frexp例程,它将以数学方式返回分数和指数而不是编码.首先,分别处理零,无穷大和NaN.然后使用:
int exponent;double fraction = frexp(value,&exponent);
这将分数设置为幅度为[½,1]且指数的值,使得分数•2指数等于值. (注意,分数仍然有符号;您可能希望将其分开并使用绝对值.)
此时,您可以根据需要缩放分数(并相应地调整指数).要缩放它以使其为奇数,您可以将其重复乘以2,直到它没有小数部分.
总结以上是内存溢出为你收集整理的尾数标准化C#double全部内容,希望文章能够帮你解决尾数标准化C#double所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)