- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
internal static double GetExtendedFromBytes(byte[] bytes)
{
/*
1 15 1 63
s e i f
The value v of the number is given by:
if 0 <= e < 32767, then v = (-1)s * 2(e-16383) * (i.f)
if e = 32767 and f = 0, then v = (-1)s * Inf
if e = 32767 and f <> 0, then v is a NaN
*/
const int memSize = 10;
if (bytes == null)
throw new ArgumentNullException("bytes");
if (bytes.Length != memSize)
throw new ArgumentException("Must be " + memSize + " bytes", "bytes");
int s = bytes[0] >> 7;
int e = 0x7FFF & ((bytes[0] << 8) | bytes[1]);
int i = bytes[2] >> 7;
ulong f = (ulong)(0x7F & bytes[2]);
for (int j = 3; j < memSize; j++)
{
f <<= 8;
f |= bytes[j];
}
decimal df = (decimal)f / 10000000000000000000 /* 10^19 */; // число f в формате 0.f
double v;
if (0 <= e && e < 32767)
{
int pow = e - 16383;
decimal c = (s == 0 ? 1 : -1) * (decimal)Math.Pow(2, pow);
decimal dv = c * (i + df); // значение, полученное по формуле для x86 Extended Precision Format
if (f != 0) // HACK при вычислении по формуле искомое значение не удается получить - исправляем
{
const decimal mn = 0.0776627963145224192m; // magic number, при значениях отличных от 2^x возникает разница кратная этому значению
decimal delta = Math.Abs(dv - c);
decimal add = 0;
if (pow >= 4)
add = Math.Round(delta * 1.0842021724855044340074528009m); // еще magic number
else if (pow >= 2)
add = Math.Ceiling(delta);
else if (pow >= 0)
add = Math.Ceiling(delta * 10) / 10m;
else
{
decimal m = 10m * (decimal)Math.Pow(2, Math.Abs(pow));
add = Math.Ceiling(delta * m) / m;
}
if (dv > 0)
dv += add * mn;
else
dv -= add * mn;
}
v = (double)dv;
}
else if (e == 32767)
{
if (f == 0)
v = s == 0 ? double.PositiveInfinity : double.NegativeInfinity;
else
v = double.NaN;
}
else
{
throw new ArgumentOutOfRangeException("bytes");
}
return v;
}
Местные индусы постарались. Перевод 80 bit floating point в double. Причём если прочитать спецификацию IEEE-754, то код займет 3-4 строчки с простыми битовыми операциями.