- 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 строчки с простыми битовыми операциями.
guest 14.09.2012 13:17 # 0
3.14159265 14.09.2012 13:23 # +2
На асме это в одну-две строки пишется. fst, fld и всё.
bormand 14.09.2012 19:07 # 0
inkanus-gray 14.09.2012 13:29 # 0
2. В самом начале проверка длины массива. Значит, принципиально требовалось вводить данные массивом?
3. Бросились в глаза сдвиги на восемь. А вдруг это для того, чтобы код одинаково работал и на платформе big endian, и на little endian?
4. Math.Pow(2, pow) — что-то страшноватое.
5. Магические константы — совсем страшно стало.
guest 14.09.2012 18:25 # 0
inkanus-gray 14.09.2012 18:59 # 0
http://msdn.microsoft.com/ru-ru/library/cs7y5x0x(v=vs.90).aspx
someone 15.09.2012 10:53 # 0
bormand 15.09.2012 13:58 # 0
Вот так вроде прокатит (не протестировано на денормалах, ибо лень).
bormand 15.09.2012 14:47 # +1
Вот этот код (в теории) должен корректно превратить денормалы в 0 с соответствующим знаком, и не должен генерить мусорных денормалов и NaNов при предельных для double значениях экспоненты. Проверять уже лень.
P.S. Остается еще баг - значения extended, которые не помещаются в нормализованную форму double, но влезли бы в денормал, данным кодом превращаются в 0, что не совсем корректно.
bormand 15.09.2012 14:54 # 0