1. C# / Говнокод #11757

    +141

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 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 строчки с простыми битовыми операциями.

    Запостил: NetDeveloper, 12 Сентября 2012

    Комментарии (10) RSS

    • Че, никто коментировать не будет?
      Ответить
      • А што тут комментировать? Говно адское. Придумали себе проблему и решают её хаками.
        На асме это в одну-две строки пишется. fst, fld и всё.
        Ответить
        • Оно и на шарпе получится не слишком длинное. С первого раза загуглилась функция BitConverter.Int64BitsToDouble. Остается написать немного битовых операций, которые соберут инт64 из того входного массива, немного подрезав мантиссу и порядок.
          Ответить
      • 1. В C# нет аналога сишного long double или паскалевского extended?
        2. В самом начале проверка длины массива. Значит, принципиально требовалось вводить данные массивом?
        3. Бросились в глаза сдвиги на восемь. А вдруг это для того, чтобы код одинаково работал и на платформе big endian, и на little endian?
        4. Math.Pow(2, pow) — что-то страшноватое.
        5. Магические константы — совсем страшно стало.
        Ответить
    • .NOT не может в REAL(10)? Фу.
      Ответить
      • Похоже, что и правда не может:
        http://msdn.microsoft.com/ru-ru/library/cs7y5x0x(v=vs.90).aspx
        Ответить
    • А как нормально сделать? Код покажете?
      Ответить
      • http://ideone.com/55cFW

        Вот так вроде прокатит (не протестировано на денормалах, ибо лень).
        Ответить
        • http://ideone.com/Y3c1l

          Вот этот код (в теории) должен корректно превратить денормалы в 0 с соответствующим знаком, и не должен генерить мусорных денормалов и NaNов при предельных для double значениях экспоненты. Проверять уже лень.

          P.S. Остается еще баг - значения extended, которые не помещаются в нормализованную форму double, но влезли бы в денормал, данным кодом превращаются в 0, что не совсем корректно.
          Ответить
          • P.S. Второй потенциальный баг - извращенные NaN'ы, у которых установленные биты не вылезают за пределы младших 11 бит мантиссы. Такой NaN превратится в Inf.
            Ответить

    Добавить комментарий