Округление чисел в МТ4 через NormalizeDouble - страница 16

 
fxsaber:
Устарел

В стандартной библиотеке есть функция нормализации цены с учётом грануляции

//+------------------------------------------------------------------+
//| Normalize price                                                  |
//+------------------------------------------------------------------+
double CSymbolInfo::NormalizePrice(const double price) const
  {
   if(m_tick_size!=0)
      return(NormalizeDouble(MathRound(price/m_tick_size)*m_tick_size,m_digits));
//---
   return(NormalizeDouble(price,m_digits));
  }
 
Slawa:

В стандартной библиотеке есть функция нормализации цены с учётом грануляции

В курсе этой крайне медленной реализации. Речь шла о доработке NormalizeDouble  (и Справки) к текущих реалиям. Написать свои грануляции каждый может.
 
transcendreamer:
я уже начал догадываться что NormalizeDouble(new_lot-sum_lots,Lots_Digits); не выдает точно 0 а сохраняет какой-то хвост

Если переменные new_lot и sum_lots равны, то разность будет точно 0. Но неизвестно, как вы их вычисляете, при вычислениях они действительно могут оказаться неравными, отсюда и ненулевая разница. Делайте то же самое так:

NormalizeDouble(new_lot, Lots_Digits) - NormalizeDouble(sum_lots, Lots_Digits).

Если переменные равны в пределах указанного числа знаков, то разница будет строго 0. 

 
Sergei Vladimirov:

Если переменные new_lot и sum_lots равны, то разность будет точно 0. Но неизвестно, как вы их вычисляете, при вычислениях они действительно могут оказаться неравными, отсюда и ненулевая разница. Делайте то же самое так:

NormalizeDouble(new_lot, Lots_Digits) - NormalizeDouble(sum_lots, Lots_Digits).

Если переменные равны в пределах указанного числа знаков, то разница будет строго 0. 

о, вот спасибо!
 
transcendreamer:

И снова про округление......

подскажите пожалуйста по ситуации (только не кидайте помидорами, я гуманитарий),

имеется такая переменная:

      double delta=NormalizeDouble(new_lot-sum_lots,Lots_Digits);

      if(delta>0) delta-=OrderLots();

      if(delta<0) delta+=OrderLots();

по идее delta изначально нормализирована,

OrderLots наверное должен возвращать нормализированные даблы,

но каким-то образом иногда в редких случаях я получаю числа вроде 2.775557561562891e-17

то есть это почти нуль но не нуль.......

первый вопрос - это нормально?

... 


Перечитал ещё раз внимательнее. Нифига это не нормально. Если вот это описание верно:

Ilyas:

Работа функции NormalizeDouble:

  1. Выделяется целая часть     - I
  2. Выделяется дробная часть - F
  3. F = F * 10^digits
  4. F = F (+ или - в зависимости от знака) 0.5
  5. F = (целая часть из F) / 10^digits
  6. результат = I + F
то и результат NormalizeDouble(new_lot - sum_lots, Lots_Digits) должен быть строго нулевым, если new_lot и sum_lots равны в пределах указанного числа знаков. В редких случаях может быть разница в 1 в последнем знаке (причина - в пунктах 4 и 5), но результат 2.775557561562891e-17 - это странно, такого быть не должно.
 

Я написал небольшой обучающий код (самому было интересно поковырять), который выворачивает наружу внутренности плавающего числа. Если кому интересно, то можно позапускать (код C++, можно на каком-нибудь онлайн компиляторе. Здесь https://goo.gl/tP691X, например)

#include <iostream>
using namespace std;

int main()
{
    cout.precision(17);
    float f = 0.5;
    //float add = 0.00000002980232239; //-25   1/(2^25)
    float add = 0.00000005960464478; //-24     1/(2^24)
    //float add = 0.0000001192092896; //-23   1/(2^23)
    //float add = 0.0000002384185791; // -22  1/(2^22)
    f+= add;

    cout << "value = " << f << endl;
    cout << "----------\n";

    char *c = (char*)&f;  // char может ссылаться на любой тип
    cout << "mantissa:\n";
    cout << "implicit_1 ";
    cout << (c[2] >> 6 & 0b1) << ' '; cout << (c[2] >> 5 & 0b1) << ' ';
    cout << (c[2] >> 4 & 0b1) << ' '; cout << (c[2] >> 3 & 0b1) << ' ';
    cout << (c[2] >> 2 & 0b1) << ' '; cout << (c[2] >> 1 & 0b1) << ' ';
    cout << (c[2] >> 0 & 0b1) << ' '; cout << (c[1] >> 7 & 0b1) << ' ';
    cout << (c[1] >> 6 & 0b1) << ' '; cout << (c[1] >> 5 & 0b1) << ' ';
    cout << (c[1] >> 4 & 0b1) << ' '; cout << (c[1] >> 3 & 0b1) << ' ';
    cout << (c[1] >> 2 & 0b1) << ' '; cout << (c[1] >> 1 & 0b1) << ' ';
    cout << (c[1] >> 0 & 0b1) << ' '; cout << (c[0] >> 7 & 0b1) << ' ';
    cout << (c[0] >> 6 & 0b1) << ' '; cout << (c[0] >> 5 & 0b1) << ' ';
    cout << (c[0] >> 4 & 0b1) << ' '; cout << (c[0] >> 3 & 0b1) << ' ';
    cout << (c[0] >> 2 & 0b1) << ' '; cout << (c[0] >> 1 & 0b1) << ' ';
    cout << (c[0] >> 0 & 0b1) << '\n';
    cout << "exponenta E - 127:\n";
    cout << "1= " << (c[2] >> 7 & 0b1) << '\n';
    cout << "2= " << (c[3] >> 0 & 0b1) << '\n';
    cout << "4= " << (c[3] >> 1 & 0b1) << '\n';
    cout << "8= " << (c[3] >> 2 & 0b1) << '\n';
    cout << "16= " << (c[3] >> 3 & 0b1) << '\n';
    cout << "32= " << (c[3] >> 4 & 0b1) << '\n';
    cout << "64= " << (c[3] >> 5 & 0b1) << '\n';
    cout << "128= " << (c[3] >> 6 & 0b1) << '\n';
    cout << "sign\n";
    cout << (c[3] >> 7 & 0b00000001) << '\n';
}

Выхлоп при f == 0.5 + 1/(2^24). 1/(2^24) - самый младший разряд мантиссы при данной степени:

value = 0.50000005960464478
----------
mantissa:
implicit_1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
exponenta E - 127:
1= 0
2= 1
4= 1
8= 1
16= 1
32= 1
64= 1
128= 0
sign
0

Степень = 126 - 127 = -1

Мантисса = 1,00000000000000000000001

Сдвигаем мантиису на степень -1 = 0,100000000000000000000001 =  1^(-1) + 1^(-24) = 1/(2^1) +  1/(2^24) = 0.5 + 0,00000005960464478 = 0,50000005960464478


В качестве теории https://habrahabr.ru/post/112953/.

ЗЫ: этот онлайн компилятор поприличней будет http://rextester.com/l/cpp_online_compiler_gcc

 
pavlick_:

Я написал небольшой обучающий код (самому было интересно поковырять), который выворачивает наружу внутренности плавающего числа. Если кому интересно, то можно позапускать (код C++, можно на каком-нибудь онлайн компиляторе. Здесь https://www.tutorialspoint.com/compile_cpp11_online.php, например)

Можно запустить и на MQL5 - заменить c[Pos] на _R(f)[(char)Pos] (или _R(f).Bytes[Pos]), подключив эту библиотеку.

ЗЫ

#include "fmtprntl.mqh"    // https://www.mql5.com/en/blogs/post/680570
#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

#define c _R(f).Bytes
#define endl "\n"

void OnStart()
{
  OutputStream cout(16, ' ');
  
  float f = 0.5;
  //float add = 0.00000002980232239; //-25   1/(2^25)
  float add = 0.00000005960464478; //-24     1/(2^24)
  //float add = 0.0000001192092896; //-23   1/(2^23)
  //float add = 0.0000002384185791; // -22  1/(2^22)
  f+= add;

  cout << "value = " << f << endl;
  cout << "----------\n";

  cout << "mantissa:\n";
  cout << "implicit_1 ";
  cout << (c[2] >> 6 & 1) << ' '; cout << (c[2] >> 5 & 1) << ' ';
  cout << (c[2] >> 4 & 1) << ' '; cout << (c[2] >> 3 & 1) << ' ';
  cout << (c[2] >> 2 & 1) << ' '; cout << (c[2] >> 1 & 1) << ' ';
  cout << (c[2] >> 0 & 1) << ' '; cout << (c[1] >> 7 & 1) << ' ';
  cout << (c[1] >> 6 & 1) << ' '; cout << (c[1] >> 5 & 1) << ' ';
  cout << (c[1] >> 4 & 1) << ' '; cout << (c[1] >> 3 & 1) << ' ';
  cout << (c[1] >> 2 & 1) << ' '; cout << (c[1] >> 1 & 1) << ' ';
  cout << (c[1] >> 0 & 1) << ' '; cout << (c[0] >> 7 & 1) << ' ';
  cout << (c[0] >> 6 & 1) << ' '; cout << (c[0] >> 5 & 1) << ' ';
  cout << (c[0] >> 4 & 1) << ' '; cout << (c[0] >> 3 & 1) << ' ';
  cout << (c[0] >> 2 & 1) << ' '; cout << (c[0] >> 1 & 1) << ' ';
  cout << (c[0] >> 0 & 1) << '\n';
  cout << "exponenta E - 127:\n";
  cout << "1= " << (c[2] >> 7 & 1) << '\n';
  cout << "2= " << (c[3] >> 0 & 1) << '\n';
  cout << "4= " << (c[3] >> 1 & 1) << '\n';
  cout << "8= " << (c[3] >> 2 & 1) << '\n';
  cout << "16= " << (c[3] >> 3 & 1) << '\n';
  cout << "32= " << (c[3] >> 4 & 1) << '\n';
  cout << "64= " << (c[3] >> 5 & 1) << '\n';
  cout << "128= " << (c[3] >> 6 & 1) << '\n';
  cout << "sign\n";
  cout << (c[3] >> 7 & 1) << '\n';
}

Результат

2016.09.27 23:56:01.178 Test (Si-12.16,H1)       0
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      sign
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      128=  0
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      64=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      32=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      16=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      8=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      4=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      2=  1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)       1=  0
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      exponenta E - 127:
2016.09.27 23:56:01.178 Test (Si-12.16,H1)       implicit_1  0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 1
2016.09.27 23:56:01.178 Test (Si-12.16,H1)       mantissa:
2016.09.27 23:56:01.178 Test (Si-12.16,H1)       ----------
2016.09.27 23:56:01.178 Test (Si-12.16,H1)      value =  0.5000000596046448 
 
implicit_1  0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 0 32 1

А что с мантиссой случилось?

32 - это ascii код проблела. Похоже библиотека с ошибкой не имеет версии для char. Думаю, надо удалить проблелы между значениями мантиссы. Или может вместо ' ' писать " " ?

 
pavlick_:

А что с мантиссой случилось?

32 - это ascii код проблела. Похоже библиотека с ошибкой. Думаю, надо удалить проблелы между значениями мантиссы. Или может вместо ' ' писать " " ?

fmtprntl.mqh - не моя, поэтому не могу точно сказать.

Не охото заморачиваться, поэтому для Вашего f распечатал значения байтов

2016.09.28 00:34:09.813 Test (Si-12.16,H1)      c[0] = 1
2016.09.28 00:34:09.813 Test (Si-12.16,H1)      c[1] = 0
2016.09.28 00:34:09.813 Test (Si-12.16,H1)      c[2] = 0
2016.09.28 00:34:09.813 Test (Si-12.16,H1)      c[3] = 63
 
Slawa:

Сопутствующий побочный эффект.

Оказалось удобно. Но на такое использование изначально не рассчитывали

Для вывода вещественного числа на печать с нужной точностью есть специальные функции.

Скажите, зачем нужно округлять вещественные числа в процессе вычислений? Ведь в этом случае теряется точность вычислений!

речь идет о корректном сравнении цен, стопов, лотов

здесь нужна определенная точность