Маленькая засада с вещественными числами

5 (1)
  • Развернуть Маленькая засада с вещественными числами ( fplab  04.02.2015 10:55 )
    5(1)
    Следующий код
    VAR a, b, z;
    a = 0.7;
    b = 0.1;
    z = INT ((a + b) * 10);
    MSGBOX (z);
    
    выводит на экран 7, хотя, очевидно, должно быть 8. Причина - в представлении чисел. Достаточно чуточку изменить
    VAR a, b, z;
    a = 0.7000000001;
    b = 0.1;
    z = INT ((a + b) * 10);
    MSGBOX (z);
    
    и тогда все нормально - выводится 8.
    Будьте внимательны, коллеги :)
    >> Ответить
    • Развернуть Очень старые грабли, поддержка советует NUMERIC, т.е. a = $0.7; b = $0.1; ( OldFox  04.02.2015 12:01 )
      5(1)
      Цитата:
      Почему выражение println (int(100 * 1.13)); дает в результате 112?

      это проблема не RSL, поскольку аналогичный пример на Си тоже дает 112, это всемирный заговор машин. :)
      А точнее - особенности представления вещественных чисел в информационно-вычислительных системах.
      http://www.delphikingdom.com/asp/viewitem.asp?catalogid=374
      Когда Вы, выполняя подобные вычисления, не определяете тип, то число 1.13 по умолчанию считается типом DOUBLE.
      Внутри процессорных обработок, при работе с вещественными числами, число 1.13 представляется в двоичной системе в виде степеней двойки.
      О том, как это делается, можно почитать по ссылке http://vestikinc.narod.ru/AB/ni_bin.htm
      целая часть - это 1 * 2^0
      дробная часть
      0,13 * 2 = 0,26
      0,26 * 2 = 0,52
      0,52 * 2 = 1,04
      0,04 * 2 = 0,08
      0,08 * 2 = 0,16
      0,16 * 2 = 0,32
      0,32 * 2 = 0,64
      0,64 * 2 = 1,28
      0,28 * 2 = 0,56
      0,56 * 2 = 1,12
      Можно продолжать дальше до бесконечности. Это повлияет в результате на точность, но для того, чтобы проиллюстрировать картину, уже достаточно.
      В итоге, в двоичной системе это число будет представлено как 1,0010000101.
      Если теперь перевести это число обратно в десятичную систему, то переводиться оно будет по такой схеме:
      1 * 2^0 + 0 * 2^-1 + 0 * 2^-2 + 1 * 2^-3 + 0 * 2^-4 + 0 * 2^-5 + 0 * 2^-6 + 0 * 2^-7 + 1 * 2^-8 + 0 * 2^-9 + 1 * 2^-10 = 1 + 1 * 2^-3 + 1 * 2^-8 + 1 * 2^-10 = 1 + 0.125 + 0.00390625 + 0.0009765625 = 1.1298828125.

      При бОльшей степени точности получится 1,1299999999999, что при умножении на 100 и отбрасывании дробной части как раз и дает 112.

      В RSL эта проблема решена для типов NUMERIC и MONEY. Тип DOUBLE не дает достаточной точности при вычислениях.
      Поэтому, во-первых, объявляйте все переменные, с которыми Вы работаете, а во-вторых, работайте с типом NUMERIC.

      >> Ответить
    • Развернуть Очень старые грабли, поддержка советует NUMERIC, т.е. a = $0.7; b = $0.1; ( OldFox  04.02.2015 12:01 )
      5(1)
      Цитата:
      Почему выражение println (int(100 * 1.13)); дает в результате 112?

      это проблема не RSL, поскольку аналогичный пример на Си тоже дает 112, это всемирный заговор машин. :)
      А точнее - особенности представления вещественных чисел в информационно-вычислительных системах.
      http://www.delphikingdom.com/asp/viewitem.asp?catalogid=374
      Когда Вы, выполняя подобные вычисления, не определяете тип, то число 1.13 по умолчанию считается типом DOUBLE.
      Внутри процессорных обработок, при работе с вещественными числами, число 1.13 представляется в двоичной системе в виде степеней двойки.
      О том, как это делается, можно почитать по ссылке http://vestikinc.narod.ru/AB/ni_bin.htm
      целая часть - это 1 * 2^0
      дробная часть
      0,13 * 2 = 0,26
      0,26 * 2 = 0,52
      0,52 * 2 = 1,04
      0,04 * 2 = 0,08
      0,08 * 2 = 0,16
      0,16 * 2 = 0,32
      0,32 * 2 = 0,64
      0,64 * 2 = 1,28
      0,28 * 2 = 0,56
      0,56 * 2 = 1,12
      Можно продолжать дальше до бесконечности. Это повлияет в результате на точность, но для того, чтобы проиллюстрировать картину, уже достаточно.
      В итоге, в двоичной системе это число будет представлено как 1,0010000101.
      Если теперь перевести это число обратно в десятичную систему, то переводиться оно будет по такой схеме:
      1 * 2^0 + 0 * 2^-1 + 0 * 2^-2 + 1 * 2^-3 + 0 * 2^-4 + 0 * 2^-5 + 0 * 2^-6 + 0 * 2^-7 + 1 * 2^-8 + 0 * 2^-9 + 1 * 2^-10 = 1 + 1 * 2^-3 + 1 * 2^-8 + 1 * 2^-10 = 1 + 0.125 + 0.00390625 + 0.0009765625 = 1.1298828125.

      При бОльшей степени точности получится 1,1299999999999, что при умножении на 100 и отбрасывании дробной части как раз и дает 112.

      В RSL эта проблема решена для типов NUMERIC и MONEY. Тип DOUBLE не дает достаточной точности при вычислениях.
      Поэтому, во-первых, объявляйте все переменные, с которыми Вы работаете, а во-вторых, работайте с типом NUMERIC.

      >> Ответить
      • Развернуть Спасибо ( fplab  04.02.2015 12:34 )
        5(1)
        Да я то в курсе. А вот коллега, недавно столкнувшийся с этим, попался.
        И совершенно верно, что RSL тут не причем - "это все придумал Черчилль в восемнадцатом году" :)
        >> Ответить
    • Развернуть или вместо Int( иcпользовать int(round( ( OldFox  04.02.2015 12:11 )
      5(1)
      z = INT( ROUND( ((a + b) * 10), 0) ) ;
      >> Ответить