[英]Mantissa Normalization of C# double
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. 编辑:现在让它工作,同时规范化mantiss,首先设置隐含位是很重要的,当解码隐含位然后不必添加。 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: 所以,我可以通过使用以下方式从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;
(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: 但是,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.
在规范编码规则和可分辨编码规则中,规定了归一化,并且尾数(除非它是0)需要重复移位,直到最低有效位为1。
(see here in section 8.5.6.5 ). (见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).
(即使使用Jon Skeet在上述链接中发布的整个功能)。
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". 我似乎在这里遗漏了一些东西,如果我第一次能够规范化双重的mantiassa并得到“位”,那将是最简单的。 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: 编辑:实际工作问题显示我的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
When you use BitConverter.DoubleToInt64Bits
, it gives you the double
value already encoded in IEEE 754 format. 当您使用
BitConverter.DoubleToInt64Bits
,它会为您提供已经以IEEE 754格式编码的double
BitConverter.DoubleToInt64Bits
值。 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.
(“有效”是浮点值的小数部分的首选术语,用于IEEE 754.有效数是线性的。尾数是对数的。“尾数”源于人们不得不使用对数和纸的日子。和粗略计算的函数表。)要恢复未编码的有效数,您必须恢复隐含位。
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:
一旦你将符号位,编码的指数(作为整数)和编码的有效数字(作为整数)分开,那么,对于64位二进制浮点:
(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 2 52 instead of to one.) (如果你想使用整数而不是有效数的分数,你可以省略乘法2 -52并将-52加到指数上。在最后一种情况下,有效数加到2 52而不是1 。)
There is an alternative method that avoids BitConverter
and the IEEE-754 encoding. 有一种替代方法可以避免
BitConverter
和IEEE-754编码。 If you can call the frexp
routine from C#, it will return the fraction and exponent mathematically instead of as encodings. 如果你可以从C#调用
frexp
例程,它将以数学方式返回分数和指数而不是编码。 First, handle zeroes, infinities, and NaNs separately. 首先,分别处理零,无穷大和NaN。 Then use:
然后使用:
int exponent;
double fraction = frexp(value, &exponent);
This sets fraction
to a value with magnitude in [½, 1) and exponent
such that fraction
•2 exponent
equals value
. 这将
fraction
设置为幅度为[½,1]且exponent
,使得fraction
•2 exponent
等于value
。 (Note that fraction
still has the sign; you might want to separate that and use the absolute value.) (注意,
fraction
仍然有符号;您可能希望将其分开并使用绝对值。)
At this point, you can scale fraction
as desired (and adjust exponent
accordingly). 此时,您可以根据需要缩放
fraction
(并相应地调整exponent
)。 To scale it so that it is an odd integer, you could multiply it by two repeatedly until it has no fractional part. 要缩放它以使其为奇数,您可以将其重复乘以2,直到它没有小数部分。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.