Alternative implementations of standard functions/approaches - page 3

 
fxsaber:
It turns out that #import ex5 is the evil of optimization.

In terms of being able to optimise globally, yes.

We have quite aggressive inlining without trying to make the code smaller. So in global optimization mode we generate very good code.

This can be seen in the compile time, where we put the resulting speed at the top of the list.

 
prostotrader:

fxsaber

There is an error in your code

Thank you, I have corrected it.
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];

  return((long)((Value > 0) ? Value / point + HALF_PLUS : Value / point - HALF_PLUS) * point);
}
 
fxsaber:
Thank you, corrected.

Still a mistake.

It should be a 5 at the end.

 
prostotrader:

Still an error

It's not an error, it's rounding. This is exactly what the standard version does.
 
fxsaber:
This is not an error, it is rounding. That's how the standard version does it.
I made a mistake with the wrong screen, look again (replace image)
 

Here's the code for you to test

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#define  EPSILON (1.0 e-7 + 1.0 e-13)
#define  HALF_PLUS  (0.5 + EPSILON)
#property indicator_separate_window
#property indicator_plots   1
#property indicator_buffers 1
double d_value = 12345.012345;
//
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];

  return((long)((Value > 0) ? Value / point + HALF_PLUS : Value / point - HALF_PLUS) * point);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   double new_value = MyNormalizeDouble(d_value, 0);
   new_value = NormalizeDouble(d_value, 0);
   new_value = MyNormalizeDouble(d_value, 1);
   new_value = NormalizeDouble(d_value, 1);
   new_value = MyNormalizeDouble(d_value, 2);
   new_value = NormalizeDouble(d_value, 2);
   new_value = MyNormalizeDouble(d_value, 3);
   new_value = NormalizeDouble(d_value, 3);
   new_value = MyNormalizeDouble(d_value, 4);
   new_value = NormalizeDouble(d_value, 4);
   new_value = MyNormalizeDouble(d_value, 5);
   new_value = NormalizeDouble(d_value, 5);
   new_value = MyNormalizeDouble(d_value, 6);
   new_value = NormalizeDouble(d_value, 6);
   new_value = MyNormalizeDouble(d_value, 7);
   new_value = NormalizeDouble(d_value, 7);
   new_value = MyNormalizeDouble(d_value, 8);
   new_value = NormalizeDouble(d_value, 8);
   new_value = MyNormalizeDouble(d_value, 9);
   new_value = NormalizeDouble(d_value, 9);
   if (new_value ==0.0)
   {}  
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
prostotrader:

Here's your code, test it.

void OnStart( void )
{
  double Val = 12345.012345;
  
  double Val2 = Val / 1.0 e-5;
  Val2 += HALF_PLUS; // Val2 == 1234501235.0
  
  long Val3 = (long)Val2; // Val3 == 1234501234
    
  return;
};

Val2 - correct. Val3 after conversion to long - not correct. Apparently, it is some peculiarity of double representation of floating point numbers. We need to increment EPSILON. I can't make it out on my sleepy head. Maybe some knowledgeable people may give me a hint.

I need to figure out what considerations the developers used to write this

//+------------------------------------------------------------------+
//| Сравнивает два значения типа double.                             |
//| RESULT                                                           |
//|   Возвращает истину, если значения равны и                       |
//|   ложь в противном случе.                                        |
//+------------------------------------------------------------------+
bool CEnvironment::DoubleEquals(const double a,const double b)
  {
//---
   return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)));
//---
  }

This seems to be where the dog is buried.

 
Now it always works correctly, but only 10% faster than the original
double MyNormalizeDouble( const double Value, const uint digits )
{
  static const double Points[] = {1.0 e-0, 1.0 e-1, 1.0 e-2, 1.0 e-3, 1.0 e-4, 1.0 e-5, 1.0 e-6, 1.0 e-7, 1.0 e-8};
  const double point = digits > 8 ? 1.0 e-8 : Points[digits];
  const long Integer = (long)Value; // чтобы не создавать крайне медленный относительный epsilon

  return((long)((Value > 0) ? (Value - Integer) / point + HALF_PLUS : (Value - Integer) / point - HALF_PLUS) * point + Integer);
}
prostotrader, Thanks for finding the discrepancies!
 
fxsaber:

This seems to be where the dog is buried.

The roots grow from the RSDN forum

DBL_EPSILON defines a difference of 1 (one!) significant bit of an exponent when applied to the number 1.0. In practice, there is no such difference - the numbers are either strictly equal or can differ by more than one significant bit. Therefore, you have to take something like 16*DBL_EPSILON to ignore the difference of 4 least significant bits (or about one and a half last significant decimal digits out of about 16 available).

Of course, there are cases where the range of numbers is more or less known and predictable. Say, 0...1000. In this case, you can take a constant like 1000*16*DBL_EPSILON for a rough comparison. But we must keep in mind that such a comparison actually turns the whole floating point idea into a fixed point (guess why).

 

A variant of CopyTicks, which is sometimes several orders of magnitude faster than the original (from > 0)

int MyCopyTicks( const string Symb, MqlTick& Ticks[], const uint flags = COPY_TICKS_ALL, const ulong from = 0, const uint count = 0 )
{
  int Res = (from == 0) ? CopyTicks(Symb, Ticks, flags, 0, count) : -1;
  
  if (from > 0)
  {    
    uint count2 = 1;
    
    MqlTick NewTicks[];    
    int Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    
    while ((Amount > 0) && ((ulong)NewTicks[0].time_msc >= from))
    {
      count2 <<= 1;
      
      Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    }

    for (int i = 1; i < Amount; i++)
      if ((ulong)NewTicks[i].time_msc >= from)
      {
        Res = ArrayCopy(Ticks, NewTicks, 0, i, (count == 0) ? 2000 : count);
        
        break;
      }    
  }
  
  return(Res);
}