尾数标准化C#double
编辑:现在让它工作,同时规范化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; (使用here的代码). 但是,DER编码规则指定应该对尾数进行规范化:
(见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并将-52加到指数中.在最后一种情况下,有效数加到252而不是1. ) 有一种替代方法可以避免BitConverter和IEEE-754编码.如果你可以从C#调用frexp例程,它将以数学方式返回分数和指数而不是编码.首先,分别处理零,无穷大和NaN.然后使用: int exponent; double fraction = frexp(value,&exponent); 这将分数设置为幅度为[?,1]且指数的值,使得分数?2指数等于值. (注意,分数仍然有符号;您可能希望将其分开并使用绝对值.) 此时,您可以根据需要缩放分数(并相应地调整指数).要缩放它以使其为奇数,您可以将其重复乘以2,直到它没有小数部分. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |