我目前正在实现一种编码(专有编码规则),并且在编码双值时会遇到一些问题。
所以,我可以使用以下方法从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编码规则指定应该对尾数进行标准化:
在 规范了规范编码规则和可分辨编码规则规范化,并且尾数(除非它是0)需要 反复移位直到最低有效位为1。
使用以下方式手动完成:
while ((mantissa & 1) == 0)
{
mantissa >>= 1;
exponent++;
}
不起作用,给我奇怪的价值观。 (即使使用Jon Skeet在上述链接中发布的整个功能)。
我似乎在这里遗漏了一些东西,如果我首先能够规范化双重的手段并获得“位”,那将是最简单的。但是,我也无法真正理解为什么手动标准化将无法正常工作。
感谢您的帮助,
丹尼
编辑:显示我的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
答案 0 :(得分:3)
当您使用BitConverter.DoubleToInt64Bits
时,它会为您提供已经以IEEE 754格式编码的double
值。这意味着有效数据用隐式前导位编码。 (“有效”是浮点值的小数部分的首选术语,用于IEEE 754.有效数是线性的。尾数是对数的。“尾数”源于人们不得不使用对数和纸的日子。和粗略计算的函数表。)要恢复未编码的有效数,你必须恢复隐含位。
这并不难。一旦你将符号位,编码的指数(作为整数)和编码的有效数字(作为整数)分开,那么对于64位二进制浮点:
(如果你想使用整数而不是有效数的分数,你可以省略乘法2 -52 并将-52添加到指数中。在最后一种情况下,有效数据被添加到2 52 而不是一个。)
有一种替代方法可以避免BitConverter
和IEEE-754编码。如果你可以从C#调用frexp
例程,它将以数学方式返回分数和指数而不是编码。首先,分别处理零,无穷大和NaN。然后使用:
int exponent;
double fraction = frexp(value, &exponent);
这会将fraction
设置为幅度为[½,1)和exponent
的值,以便fraction
•2 exponent
等于{ {1}}。 (请注意,value
仍有符号;您可能希望将其分开并使用绝对值。)
此时,您可以根据需要缩放fraction
(并相应地调整fraction
)。要缩放它以使它是一个奇数,你可以重复乘以它,直到它没有小数部分。