Alternative implementations of standard functions/approaches - page 14

 
fxsaber #:

Thanks. But this.


PS Slow.

Also, this has round-off erros.

void OnStart()
  {
   Print(MyNormalizeDoubleSlow(1.89, 2) == 1.89);   // false
   Print(MyNormalizeDoubleSlow(1.143, 2) == 1.14);  // false
  }

Edit:

This function avoid a lot of corner cases (very fast + accurate):

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+  
double MyNormalizeDouble(const double value, const int digits)
  { 
   static const double Powers[]= {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8};
   double pwr = Powers[MathMin(digits, 8)];  
//---
   return Round((value * pwr) * (1 + DBL_EPSILON)) / pwr; 
  }

//+------------------------------------------------------------------+
//| Fast ceil, floor, and round using arithmetic operators.          |
//+------------------------------------------------------------------+
long Ceil( double v)  { long k=(long)v; return k + (v > k); }
long Floor(double v)  { long k=(long)v; return k - (v < k); }
long Round(double v)  { return (long)((v < 0) ? v - 0.5 : v + 0.5); }
long Trunc(double v)  { return (long)v; }
 
amrali #:

This function avoid a lot of corner cases (very fast + accurate):

Speeded it up a bit.

double MyNormalizeDouble(const double value, const int digits)
  { 
   static const double Powers[]= {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8};
   static const double Powers2[]= {1e0 * (1 + DBL_EPSILON),
                                   1e1 * (1 + DBL_EPSILON),
                                   1e2 * (1 + DBL_EPSILON),
                                   1e3 * (1 + DBL_EPSILON),
                                   1e4 * (1 + DBL_EPSILON),
                                   1e5 * (1 + DBL_EPSILON),
                                   1e6 * (1 + DBL_EPSILON),
                                   1e7 * (1 + DBL_EPSILON),
                                   1e8 * (1 + DBL_EPSILON)};
                                  
//   return Round(value * Powers2[digits]) / Powers[digits];
   return (long)((value > 0) ? value * Powers2[digits] + 0.5 : value * Powers2[digits] - 0.5) / Powers[digits];    
   
//---
  }

Faster than the NormalizeDouble function by 20%.

 
fxsaber #:

Speeded it up a bit.

Faster than the NormalizeDouble function by 20%.

Yes, that's an accurate algorithm ( than your first one) to round without round-off errors.

But, you use two lookup operations. For ceil and floor, you will need 3 lookup tables!

Anyway, a little cleaner:

   return (long)((value > 0 ? 0.5 : -0.5) + value * Powers2[digits]) / Powers[digits];    


Edit:

Another idea instead of lookup table for exact powers of ten, you could use the "Exponentiation by squaring (Binary exponentiation) algorithm".

This is a lot faster than MathPow(), but it is slower than the lookup table method.

//+------------------------------------------------------------------+
//| Exponentiation by Squaring (Binary exponentiation)               |
//+------------------------------------------------------------------+
// https://stackoverflow.com/a/101613
double fast_power(double base, int exponent)
{
   double result = 1.0;
   for (;;)
     {
      if ((exponent & 1) == 1)
         result *= base;
      if ((exponent >>= 1) == 0)
         break;
      base *= base;
     }
   return result;
}

double MyNormalizeDouble(const double value, const int digits)
  { 
   double pwr = fast_power(10, digits);
   return Round((value * pwr) * (1 + DBL_EPSILON)) / pwr; 
  }