Dan*_*iel 5 c# floating-point encoding
编辑:现在让它工作,同时规范化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;
Run Code Online (Sandbox Code Playgroud)
(使用此处的代码).这些值可以编码,过程的简单反转将使我恢复原来的双倍.
但是,DER编码规则指定应该对尾数进行规范化:
在规范编码规则和可分辨编码规则中,规定了归一化,并且尾数(除非它是0)需要重复移位,直到最低有效位为1.
(见8.5.6.5节).
手动执行此操作:
while ((mantissa & 1) == 0)
{
mantissa >>= 1;
exponent++;
}
Run Code Online (Sandbox Code Playgroud)
不行,并给我奇怪的价值观.(即使使用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, 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
Run Code Online (Sandbox Code Playgroud)
当您使用 时BitConverter.DoubleToInt64Bits,它会为您提供double已经以 IEEE 754 格式编码的值。这意味着尾数是用隐式前导位编码的。(\xe2\x80\x9cSignificand\xe2\x80\x9d 是浮点值小数部分的首选术语,用于 IEEE 754。有效数是线性的。尾数是对数的。\xe2\x80\x9cMantissa \xe2\x80\x9d 源于人们不得不使用对数、纸张和函数表进行粗略计算的时代。)要恢复未编码的有效数,您必须恢复隐式位。
那并不难。将符号位、编码指数(作为整数)和编码有效数(作为整数)分开后,对于 64 位二进制浮点数:
\n\n(如果您想使用整数并且尾数没有分数,则可以省略乘以 2 \xe2\x80\x9352并将 \xe2\x80\x9352 添加到指数。在最后一种情况下,尾数为添加到 2 52而不是 1。)
\n\n有一种替代方法可以避免BitConverterIEEE-754 编码。如果您可以从 C# 调用该frexp例程,它将以数学方式返回分数和指数,而不是作为编码。首先,分别处理零、无穷大和 NaN。然后使用:
int exponent;\ndouble fraction = frexp(value, &exponent);\nRun Code Online (Sandbox Code Playgroud)\n\n这设置fraction为一个大小为 [\xc2\xbd, 1) 的值,exponent使得fraction\xe2\x80\xa22exponent等于value。(请注意,fraction仍然有符号;您可能需要将其分开并使用绝对值。)
此时,您可以fraction根据需要进行缩放(并进行exponent相应调整)。要将其缩放为奇数,您可以将其重复乘以二,直到它没有小数部分。