Математическое округление. - страница 4

 
A100:
#include <Math\Stat\Math.mqh>
void OnStart()
{
        const double d = 5.4555;
        int digits = 3;
        Print( d, "->", MathRound( d, digits ), "->", ::NormalizeDouble( d, digits ));
}
Результат: 5.4555->5.455->5.456

Вот именно из-за этого и HALF_PLUS! Теперь вспомнил, так было изначально

#define HALF_PLUS  (0.5 + 1.0e-7)

Надо бы проверочный скрипт похитрее сделать, но лень пока. Здесь Ренат распинался, как надо сравнивать. А у @Quantum, действительно, ошибка.

Альтернативные реализации стандартных функций/подходов
Альтернативные реализации стандартных функций/подходов
  • www.mql5.com
NormalizeDouble Результат 1123275 и 1666643 в пользу MyNormalizeDouble (Optimize=1). Без оптимизации - быстрее раза в четыре (на память...
 

Вообще Math::MathRound какая то не предсказуемая функция

#include <Math\Stat\Math.mqh>
void OnStart()
{
        const double d1 = 5.5555,
                     d2 = 5.4555;
        int digits = 3;
        Print( d1, "->", MathRound( d1, digits ), "->", ::NormalizeDouble( d1, digits ));
        Print( d2, "->", MathRound( d2, digits ), "->", ::NormalizeDouble( d2, digits ));
}

Результат:

5.5555->5.556->5.556
5.4555->5.455->5.456
А чем d1 принципиально отличается от d2? 


 
A100:

А чем d1 принципиально отличается от d2?

В двоичной системе счисления колоссально отличаются.

0.5 == 2^(-1) .

0.05 == 2^(-x), где x - что-то страшное.

 
A100:

Вообще Math::MathRound какая то не предсказуемая функция

У меня получилась идентичная, поэтому "унаследована" непредсказуемость.
 
fxsaber:
Быстрой не знаю, но быстрее написать не сложно.
Они уже ускорили (сравнивал на x32) - проверьте пожалуйста ::NormalizeDouble на x64
 
A100:
Они уже ускорили (сравнивал на x32) - проверьте пожалуйста ::NormalizeDouble на x64
Вчера проверял. Мой вариант на 10% быстрее на x64.
 
fxsaber:
Вчера проверял. Мой вариант на 10% быстрее на x64.

10% - не считается. По сравнению с Math::MathRound разница в несколько раз

//#include <Math\Stat\Math.mqh>
void OnStart()
{
        const double d = 9.305703909421064;
        const int dd = 3;
        const int ii = 32768*64*4;
        double d12;
        double d23;
        const int time1 = GetTickCount();
        for ( int i = 0; i < ii; i++ ) d12 = MathRound( d, dd );
        const int time2 = GetTickCount();
        for ( int i = 0; i < ii; i++ ) d23 = NormalizeDouble( d, dd );
        const int time3 = GetTickCount();
        Print( time2-time1, "ms->", time3-time2, "ms" );
        printf( "", d12, d23 );
}
 
A100:

10% - не считается. По сравнению с Math::MathRound разница в несколько раз

//#include <Math\Stat\Math.mqh>
void OnStart()
{
        const double d = 9.305703909421064;
        const int dd = 3;
        const int ii = 32768*64*4;
        double d12;
        double d23;
        const int time1 = GetTickCount();
        for ( int i = 0; i < ii; i++ ) d12 = MathRound( d, dd );
        const int time2 = GetTickCount();
        for ( int i = 0; i < ii; i++ ) d23 = NormalizeDouble( d, dd );
        const int time3 = GetTickCount();
        Print( time2-time1, "ms->", time3-time2, "ms" );
        printf( "", d12, d23 );
}
2016.12.16 22:58:55.828 0ms->62ms

Это если использовать мой MathRound. Только ноль будет всегда, каким бы большим не задали ii - компилятор вырезает этот for.

Ренат давал методу подробно, как нужно правильно мерять скорость, когда оптимизатор очень умен.

Вот тест

#define HALF_PLUS  (0.5 + 1.0e-7)

double MathRound( const double Value, const uint digits )
{
  static const double Points[] = {1.0e+0, 1.0e+1, 1.0e+2, 1.0e+3, 1.0e+4, 1.0e+5, 1.0e+6, 1.0e+7, 1.0e+8, 1.0e+9, 1.0e+10, 1.0e+11, 1.0e+12, 1.0e+13, 1.0e+14, 1.0e+15, 1.0e+16};
  const double point = (digits > 16) ? 1.0e+16 : Points[digits];
  const long Integer = (long)Value; // чтобы не создавать крайне медленный относительный epsilon

  return((long)((Value > 0) ? (Value - Integer) * point + HALF_PLUS : (Value - Integer) * point - HALF_PLUS) / point + Integer);
}

ulong BenchStandard(const int Amount=1.0e8)
  {
   double       Price=1.23456;
   const double point=0.00001;
   const ulong  StartTime=GetMicrosecondCount();
//---
   for(int i=0; i<Amount;i++)
     {
      Price=NormalizeDouble(Price+point,2+(i&15));
     }

   Print("Result: ",Price);   // специально выводим результат, чтобы цикл не оптимизировался в ноль
//---
   return(GetMicrosecondCount() - StartTime);
  }

#include <Math\Stat\Math.mqh>

ulong BenchCustom(const int Amount=1.0e8)
  {
   double       Price=1.23456;
   const double point=0.00001;
   const ulong  StartTime=GetMicrosecondCount();
//---
   for(int i=0; i<Amount;i++)
     {
      Price=MathRound(Price+point,2+(i&15));
     }

   Print("Result: ",Price);   // специально выводим результат, чтобы цикл не оптимизировался в ноль
//---
   return(GetMicrosecondCount() - StartTime);
  }

void OnStart(void)
  {
   Print("Standard: ",BenchStandard()," msc");
   Print("Custom:   ",BenchCustom()," msc");
  }

Результат

Result: 1.23013
Custom:   5689482 msc
Result: 1.23013
Standard: 1653102 msc
Result: 1.23013
Custom:   1479310 msc

Выделенное - это мой MathRound, другой Custom - Quantum. Поэтому и говорю о 10%.

А вот так это же выглядит на MT4

2016.12.16 23:15:03.841 MathRound EURUSD,M1: Custom:   3024808 msc
2016.12.16 23:15:03.841 MathRound EURUSD,M1: Result: 1.23013
2016.12.16 23:15:00.819 MathRound EURUSD,M1: Standard: 3549950 msc
2016.12.16 23:15:00.819 MathRound EURUSD,M1: Result: 1.23013
2016.12.16 23:14:44.102 MathRound EURUSD,M1: Custom:   10329660 msc
2016.12.16 23:14:44.102 MathRound EURUSD,M1: Result: 1.23013

Т.е. MT5 в два раза быстрее MT4 на этом тесте.

 
x32 сохраняет цикл. Соотношения у меня примерно такие же получились (с учетом ручной отпимизации Custom)
 
A100:
x32 выполняет цикл без лишней самодеятельности. Соотношения у меня примерно такие же получились 
В общем, в текущем виде Math::MathRound лучше не использовать.
Причина обращения: