851 lines
73 KiB
MQL5
851 lines
73 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| math_utils.mqh |
|
|
//| Copyright © 2018, Amr Ali |
|
|
//| https://www.mql5.com/en/users/amrali |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright © 2018, Amr Ali"
|
|
#property link "https://www.mql5.com/en/users/amrali"
|
|
#property version "4.35"
|
|
#property description "Handy functions for comparison, rounding, formatting and debugging of doubles (prices, lots and money)."
|
|
|
|
#ifdef __MQL4__
|
|
#property strict
|
|
#endif
|
|
|
|
#ifndef MATH_UTILS_UNIQUE_HEADER_ID_H
|
|
#define MATH_UTILS_UNIQUE_HEADER_ID_H
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Functions in the library. |
|
|
//+------------------------------------------------------------------+
|
|
/*
|
|
I. Handy functions for comparison of doubles
|
|
--------------------------------------------
|
|
int Compare(const double a, const double b, const int digits);
|
|
bool EQ(const double a, const double b, const int digits = 8);
|
|
bool NE(const double a, const double b, const int digits = 8);
|
|
bool GT(const double a, const double b, const int digits = 8);
|
|
bool LT(const double a, const double b, const int digits = 8);
|
|
bool GTE(const double a, const double b, const int digits = 8);
|
|
bool LTE(const double a, const double b, const int digits = 8);
|
|
bool AlmostEqual(const double a, const double b);
|
|
bool EqualDoubles(const double a, const double b, const int significantDigits = 15);
|
|
bool IsClose(const double a, const double b, const int maxDifferentSignificantDigits = 2);
|
|
int GetEqualDigits(const double a, const double b);
|
|
int GetEqualSigDigits(const double a, const double b);
|
|
|
|
II. Handy functions for rounding of doubles
|
|
-------------------------------------------
|
|
double Ceil(const double v);
|
|
double Ceil(const double value, const int digits);
|
|
double Ceil(const double value, const double step);
|
|
double Floor(const double v);
|
|
double Floor(const double value, const int digits);
|
|
double Floor(const double value, const double step);
|
|
double Round(const double v);
|
|
double Round(const double value, const int digits);
|
|
double Round(const double value, const double step);
|
|
double Trunc(const double v);
|
|
double Trunc(const double value, const int digits);
|
|
double Trunc(const double value, const double step);
|
|
double RoundToSignificantDigits(const double value, int digits);
|
|
double RoundPrice(double pPrice, string pSymbol = NULL);
|
|
double RoundVolume(double pVolume, string pSymbol = NULL);
|
|
double StripError(const double value);
|
|
|
|
III. Miscellaneous handy functions for doubles
|
|
----------------------------------------------
|
|
bool IsInteger(const double value);
|
|
bool IsRound(const double value, const int digits);
|
|
bool IsRound(const double value, const double step);
|
|
double Frac(const double value);
|
|
int GetDigits(const double value);
|
|
int GetIntegerDigits(const double value);
|
|
int GetSignificantDigits(double value);
|
|
double Remap(const double value, const double min, const double max, const double destMin, const double destMax);
|
|
double Normalize(const double value, const double min, const double max);
|
|
double Denormalize(const double value, const double destMin, const double destMax);
|
|
T Clamp(const T value, const T min, const T max);
|
|
T ClampTop(const T value, const T max);
|
|
T ClampBot(const T value, const T min);
|
|
T Wrap(const T value, const T min, const T max);
|
|
double safeDiv(const double a, const double b);
|
|
int FloorLog10(const double value);
|
|
double GetPower10(const int power);
|
|
double MathSign(const double value);
|
|
|
|
IV. Handy functions for low-level binary operations on doubles
|
|
--------------------------------------------------------------
|
|
long DoubleToLongBits(const double value);
|
|
double LongBitsToDouble(const long bits);
|
|
int RawExponent(const double value);
|
|
long RawSignificand(const double value);
|
|
int GetExponent(const double value);
|
|
double GetSignificand(const double value);
|
|
bool IsExactDouble(const double value);
|
|
double NextAfter(const double value);
|
|
double DoubleAdvance(const double value, const long distance);
|
|
double Ulp(const double value);
|
|
long UlpDiff(const double a, const double b);
|
|
|
|
V. Handy functions for formatting of doubles to string
|
|
------------------------------------------------------
|
|
string DoubleToHexadecimal(const double value);
|
|
string DoubleToHexFloatConstant(const double value);
|
|
string DoubleToStringExact(const double value);
|
|
string DoubleToExponential(const double value, int digits = -1);
|
|
string Repr(const double value);
|
|
string Repr(const float value);
|
|
string FormatDouble(const double value, const int digits, const string separator=",");
|
|
*/
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| I. Handy functions for comparison of doubles. |
|
|
//+------------------------------------------------------------------+
|
|
|
|
// GT
|
|
//---------------------> double a
|
|
// EQ (1/2 point zone)
|
|
//---------------------> double b
|
|
// LT
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Compares two doubles and returns a value indicating whether one |
|
|
//| is nearly equal to, less than, or greater than the other. |
|
|
//+------------------------------------------------------------------+
|
|
int Compare(const double a, const double b, const int digits)
|
|
{
|
|
// bool res = MathAbs(a - b) < 0.5 * MathPow(10, -digits);
|
|
bool res = NormalizeDouble(a - b, digits) == 0;
|
|
//---
|
|
if(res)
|
|
return 0;
|
|
else
|
|
return (a > b) ? 1 : -1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Is Equal. |
|
|
//+------------------------------------------------------------------+
|
|
bool EQ(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) == 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Is Not Equal. |
|
|
//+------------------------------------------------------------------+
|
|
bool NE(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) != 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Is Greater Than. |
|
|
//+------------------------------------------------------------------+
|
|
bool GT(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) > 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Is Less Than. |
|
|
//+------------------------------------------------------------------+
|
|
bool LT(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) < 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Greater Than or Equal. |
|
|
//+------------------------------------------------------------------+
|
|
bool GTE(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) >= 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Is Less Than or Equal. |
|
|
//+------------------------------------------------------------------+
|
|
bool LTE(const double a, const double b, const int digits = 8)
|
|
{
|
|
return Compare(a, b, digits) <= 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check almost equality (ignore tiny rounoff error) |
|
|
//| If binary representations of two doubles differ by more than |
|
|
//| one least significant bit (ulp), the function returns false. |
|
|
//| For example, AlmostEqual(0.1+0.2, 0.3) => true |
|
|
//+------------------------------------------------------------------+
|
|
bool AlmostEqual(const double a, const double b)
|
|
{
|
|
// return (a == b) || MathAbs(a - b) <= DBL_EPSILON * MathMax(MathAbs(a), MathAbs(b));
|
|
return (a == b) || MathAbs(UlpDiff(a, b)) <= 1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check whether two numbers are equal up to "n" significant |
|
|
//| digits of precision (at least "n" significant digits agree). |
|
|
//| For example, EqualDoubles(3.124, 3.122, 3) => true |
|
|
//+------------------------------------------------------------------+
|
|
bool EqualDoubles(const double a, const double b, const int significantDigits = 15)
|
|
{
|
|
//--- https://stackoverflow.com/a/17382806
|
|
return (a == b) || MathAbs(a - b) <= GetPower10(-significantDigits) * MathMax(MathAbs(a), MathAbs(b));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check whether two floating point numbers are close in value. |
|
|
//| n: the max number of least significant digits that do not agree. |
|
|
//| Something like 2 or 3 usually works in practice. |
|
|
//+------------------------------------------------------------------+
|
|
bool IsClose(const double a, const double b, const int maxDifferentSignificantDigits = 2)
|
|
{
|
|
//--- https://stackoverflow.com/a/17382806
|
|
return (a == b) || MathAbs(a - b) <= GetPower10(maxDifferentSignificantDigits) * DBL_EPSILON * MathMax(MathAbs(a), MathAbs(b));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get number of fractional digits that agree after the decimal |
|
|
//| point of two numbers. GetEqualDigits(3.124, 3.122) => 2 |
|
|
//+------------------------------------------------------------------+
|
|
int GetEqualDigits(const double a, const double b)
|
|
{
|
|
//--- https://stackoverflow.com/a/49242211
|
|
return (a == b) ? 30 : MathMax(-FloorLog10(MathAbs(a - b)) - 1, 0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get number of significant digits that agree of two numbers. |
|
|
//| For example, GetEqualSigDigits(3.124, 3.122) => 3 |
|
|
//+------------------------------------------------------------------+
|
|
int GetEqualSigDigits(const double a, const double b)
|
|
{
|
|
return (a == b) ? 17 : FloorLog10(MathMax(MathAbs(a), MathAbs(b))) - FloorLog10(MathAbs(a - b));
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| II. Handy functions for rounding of doubles. |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Fast ceil, floor, and round using arithmetic operators. |
|
|
//+------------------------------------------------------------------+
|
|
double Ceil (const double v) { double k=(double)(long)v; return k + (v > k); }
|
|
double Floor(const double v) { double k=(double)(long)v; return k - (v < k); }
|
|
double Round(const double v) { return (double)(long)((v < 0) ? v - 0.5 : v + 0.5); }
|
|
double Trunc(const double v) { return (double)(long)v; }
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Round to a specified number of decimal digits. |
|
|
//+------------------------------------------------------------------+
|
|
/*
|
|
* https://stackoverflow.com/a/48764436/4208440
|
|
*
|
|
* A simple drop in solution that provides accurate decimal rounding.
|
|
* It treats doubles more like decimals by fixing the binary rounding
|
|
* issues to avoid unexpected results.
|
|
*/
|
|
double Ceil (const double value, const int digits) { double p = GetPower10(digits); return Ceil ((value * p) * (1 - DBL_EPSILON * MathSign(value))) / p; }
|
|
double Floor(const double value, const int digits) { double p = GetPower10(digits); return Floor((value * p) * (1 + DBL_EPSILON * MathSign(value))) / p; }
|
|
double Round(const double value, const int digits) { double p = GetPower10(digits); return Round((value * p) * (1 + DBL_EPSILON)) / p; }
|
|
double Trunc(const double value, const int digits) { double p = GetPower10(digits); return Trunc((value * p) * (1 + DBL_EPSILON)) / p; }
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Round to a whole multiple of some increment. |
|
|
//+------------------------------------------------------------------+
|
|
double Ceil (const double value, const double step) { return StripError(Ceil (StripError(value / step)) * step); }
|
|
double Floor(const double value, const double step) { return StripError(Floor(StripError(value / step)) * step); }
|
|
double Round(const double value, const double step) { return StripError(Round(StripError(value / step)) * step); }
|
|
double Trunc(const double value, const double step) { return StripError(Trunc(StripError(value / step)) * step); }
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Round to a specified number of significant figures. |
|
|
//+------------------------------------------------------------------+
|
|
/**
|
|
* https://en.wikipedia.org/wiki/Significant_figures
|
|
* https://github.com/php/php-src/blob/PHP-7.4.10/ext/standard/math.c#L127-L206
|
|
* https://stackoverflow.com/a/374470/4208440
|
|
*/
|
|
double RoundToSignificantDigits(const double value, int digits)
|
|
{
|
|
if(!value || !MathIsValidNumber(value))
|
|
return value;
|
|
|
|
//--- Round to significant digits
|
|
digits -= FloorLog10(value) + 1;
|
|
double p = GetPower10(MathAbs(digits));
|
|
return digits > 0 ? Round(value * p) / p : Round(value / p) * p;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Eliminate floating-point inaccuracies. Round to 15 significant |
|
|
//| figures to strip the floating-point round-off error (at the 16th |
|
|
//| significant digit) in the intermediate calculations. |
|
|
//+------------------------------------------------------------------+
|
|
double StripError(const double value)
|
|
{
|
|
//--- if the number is whole integer
|
|
if(Floor(value) == value)
|
|
{
|
|
return value;
|
|
}
|
|
//--- DBL_DIG: Number of significant decimal digits for double type = 15
|
|
return RoundToSignificantDigits(value, DBL_DIG);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Round a calculated price to the nearest tick price. |
|
|
//+------------------------------------------------------------------+
|
|
double RoundPrice(double pPrice, string pSymbol = NULL)
|
|
{
|
|
pSymbol = pSymbol == NULL ? _Symbol : pSymbol;
|
|
|
|
double ticksize = SymbolInfoDouble(pSymbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
if(ticksize == 0)
|
|
{
|
|
Print(__FUNCTION__, ": error, cannot retrieve ticksize for " + pSymbol);
|
|
return (0);
|
|
}
|
|
|
|
return Round(pPrice, ticksize);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Round a calculated volume to the nearest minlot + multiple*step. |
|
|
//+------------------------------------------------------------------+
|
|
double RoundVolume(double pVolume, string pSymbol = NULL)
|
|
{
|
|
pSymbol = pSymbol == NULL ? _Symbol : pSymbol;
|
|
|
|
double minlot = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MIN);
|
|
double maxlot = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MAX);
|
|
double lotstep = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_STEP);
|
|
|
|
if(minlot == 0 || maxlot == 0 || lotstep == 0)
|
|
{
|
|
Print(__FUNCTION__, ": error, cannot retrieve volume info for " + pSymbol);
|
|
return (0);
|
|
}
|
|
|
|
const double Epsilon = 0.000001;
|
|
|
|
pVolume -= minlot;
|
|
|
|
if(pVolume < -Epsilon)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
if(pVolume < Epsilon)
|
|
{
|
|
return (minlot);
|
|
}
|
|
|
|
pVolume = minlot + Round(pVolume, lotstep);
|
|
|
|
if(pVolume > maxlot)
|
|
{
|
|
pVolume = maxlot;
|
|
}
|
|
|
|
return (pVolume);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| III. Miscellaneous handy functions for doubles. |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Determines whether the passed value is an integer. |
|
|
//+------------------------------------------------------------------+
|
|
bool IsInteger(const double value)
|
|
{
|
|
// return (MathMod(value, 1.0) == 0)
|
|
return Round(value) == value;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if a number has a specified number of decimal digits. |
|
|
//+------------------------------------------------------------------+
|
|
bool IsRound(const double value, const int digits)
|
|
{
|
|
// return value == StringToDouble(DoubleToString(value, digits));
|
|
double p = GetPower10(digits);
|
|
return MathRound(value * p) / p == value;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if a number is a whole multiple of some increment. |
|
|
//+------------------------------------------------------------------+
|
|
bool IsRound(const double value, const double step)
|
|
{
|
|
return Round(value, step) == value;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the decimal part of x (always has the same sign as x) |
|
|
//+------------------------------------------------------------------+
|
|
double Frac(const double value)
|
|
{
|
|
return MathMod(value, 1.0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get number of decimal digits after the decimal point. |
|
|
//+------------------------------------------------------------------+
|
|
int GetDigits(const double value)
|
|
{
|
|
int d = 0;
|
|
// for(double p = 1; value != MathRound(value * p) / p && (d < 308); p *= 10)
|
|
while(!IsRound(value, d) && d < DBL_MAX_10_EXP)
|
|
d++;
|
|
return d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get number of integer digits to the left of decimal point. |
|
|
//+------------------------------------------------------------------+
|
|
int GetIntegerDigits(const double value)
|
|
{
|
|
if(!value || MathAbs(value) < 1.0)
|
|
return 0;
|
|
|
|
return FloorLog10(value) + 1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get number of significant digits. Significant digits or figures |
|
|
//| is the sum of integer and decimal digits (left and right of the |
|
|
//| decimal point), excluding leading and trailing zeros. |
|
|
//| For example, the number 1.23 has 2 decimal places and 3 s.f. |
|
|
//| Hint: Change number to scientific notation. It is easier to see. |
|
|
//+------------------------------------------------------------------+
|
|
int GetSignificantDigits(double value)
|
|
{
|
|
if(!value || !MathIsValidNumber(value))
|
|
return 0;
|
|
|
|
//--- sum of decimal and integral digits
|
|
int digits = GetDigits(value) + FloorLog10(value) + 1;
|
|
|
|
//--- excluding trailing zeros
|
|
while(MathMod(value, 10) == 0)
|
|
{
|
|
value /= 10;
|
|
digits--;
|
|
}
|
|
|
|
return digits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Linear Transformation (RESCALE) |
|
|
//| Convert x in range [min, max] to y in another range [destMin, |
|
|
//| destMax]. This can be used to rescale indicator values or |
|
|
//| graphic objects. Common scales are [0,1], [0,100] or [-1,1]. |
|
|
//+------------------------------------------------------------------+
|
|
double Remap(const double value, const double min, const double max, const double destMin, const double destMax)
|
|
{
|
|
//--- to maintain ratios
|
|
double y = safeDiv((value - min), (max - min)) * (destMax - destMin) + destMin;
|
|
return Clamp(y, destMin, destMax);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Remap value on a scale [min, max] to normalized scale [0, 1]. |
|
|
//+------------------------------------------------------------------+
|
|
double Normalize(const double value, const double min, const double max)
|
|
{
|
|
return Remap(value, min, max, 0, 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Remap normalized value [0, 1] to a scale [destMin, destMax]. |
|
|
//+------------------------------------------------------------------+
|
|
double Denormalize(const double value, const double destMin, const double destMax)
|
|
{
|
|
return Remap(value, 0, 1, destMin, destMax);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clamping (limiting) a number to boundaries (range). |
|
|
//+------------------------------------------------------------------+
|
|
template<typename T>
|
|
T Clamp(const T value, const T min, const T max)
|
|
{
|
|
//if(value < min) return min;
|
|
//if(value > max) return max;
|
|
//return value;
|
|
return MathMin(MathMax(value, min), max);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clamping (limiting) a number to a maximum value. |
|
|
//+------------------------------------------------------------------+
|
|
template<typename T>
|
|
T ClampTop(const T value, const T max)
|
|
{
|
|
return MathMin(value, max);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clamping (limiting) a number to a minimum value. |
|
|
//+------------------------------------------------------------------+
|
|
template<typename T>
|
|
T ClampBot(const T value, const T min)
|
|
{
|
|
return MathMax(value, min);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Wrap a value into range between [min, max]. |
|
|
//+------------------------------------------------------------------+
|
|
// https://stackoverflow.com/a/14416133
|
|
template<typename T>
|
|
T Wrap(const T value, const T min, const T max)
|
|
{
|
|
const T range = max - min + 1;
|
|
//while(value < min) value += range;
|
|
//while(value > max) value -= range;
|
|
//return value;
|
|
if(value < min)
|
|
return max - (T)MathMod((max - value), range);
|
|
if(value > max)
|
|
return min + (T)MathMod((value - min), range);
|
|
return value;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Avoids zero divide error that forces the mql program to stop. |
|
|
//+------------------------------------------------------------------+
|
|
double safeDiv(const double a, const double b)
|
|
{
|
|
//--- force double division.
|
|
return (b != 0) ? a / b : 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast lookup table for exact powers of 10. |
|
|
//+------------------------------------------------------------------+
|
|
const double PowersOf10[] =
|
|
{
|
|
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07,
|
|
1e08, 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
|
|
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the exponent of the scientific notation of a number. |
|
|
//| In scientific notation a number is converted to a decimal number |
|
|
//| between 1.0 and 10, multiplied by 10 raised to some power. |
|
|
//| It computes shift the decimal point to keep only one non-zero |
|
|
//| digit before the decimal point. |
|
|
//| |
|
|
//| Faster than floor(log10(n)): 3x |
|
|
//| Uses the relationship log10(x) = log2(x) * log10(2) |
|
|
//| http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 |
|
|
//+------------------------------------------------------------------+
|
|
int FloorLog10(double value)
|
|
{
|
|
int result = 0;
|
|
value = fabs(value);
|
|
|
|
/* Not in lookup table */
|
|
if (value < 1e-22 || value > 1e22)
|
|
{
|
|
result = (int) Floor(log10(value));
|
|
}
|
|
else
|
|
{
|
|
int e = GetExponent(value); /* floor(log2(x)) */
|
|
int t = ((e + 1) * 20201781) >> 26; /* log10(2) ~= 20201781/2^26 */
|
|
if(t >= 0)
|
|
result = t - (value < PowersOf10[t]); /* t may be off by one */
|
|
else
|
|
result = t - (value < 1 / PowersOf10[-t]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns pow(10, (int)power), uses fast lookup table for powers. |
|
|
//| https://github.com/php/php-src/blob/master/ext/standard/math |
|
|
//| Faster than pow(10, n): 20-30x |
|
|
//+------------------------------------------------------------------+
|
|
double GetPower10(const int power)
|
|
{
|
|
/* Not in lookup table */
|
|
if (power < -22 || power > 22)
|
|
{
|
|
return pow(10.0, (double)power);
|
|
}
|
|
if(power >= 0)
|
|
return PowersOf10[power];
|
|
|
|
return 1 / PowersOf10[-power];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Computes the sign of a value as 1, 0, -1 |
|
|
//+------------------------------------------------------------------+
|
|
/**
|
|
* It is recommended that a function return 1, 0, and -1, respectively.
|
|
*/
|
|
double MathSign(const double value)
|
|
{
|
|
return (value > 0) - (value < 0);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| IV. Handy functions for low-level binary operations on doubles. |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the bit representation corresponding to a double value . |
|
|
//+------------------------------------------------------------------+
|
|
long DoubleToLongBits(const double value)
|
|
{
|
|
union _d {double value; long bits;} dbl;
|
|
dbl.value = value;
|
|
|
|
return dbl.bits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the double value corresponding to a bit representation. |
|
|
//+------------------------------------------------------------------+
|
|
double LongBitsToDouble(const long bits)
|
|
{
|
|
union _d {double value; long bits;} dbl;
|
|
dbl.bits = bits;
|
|
|
|
return dbl.value;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the raw encoding of exponent in the bit representation. |
|
|
//+------------------------------------------------------------------+
|
|
int RawExponent(const double value)
|
|
{
|
|
return (int)((DoubleToLongBits(value) >> 52) & 0x7FF);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns raw encoding of significand in the bit representation. |
|
|
//+------------------------------------------------------------------+
|
|
long RawSignificand(const double value)
|
|
{
|
|
return (long)(DoubleToLongBits(value) & 0xFFFFFFFFFFFFF);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the unbiased (adjusted) exponent of a double value. |
|
|
//+------------------------------------------------------------------+
|
|
int GetExponent(const double value)
|
|
{
|
|
// return (int) MathFloor(MathLog(MathAbs(value)) / M_LN2);
|
|
return RawExponent(value) - 1023;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the significand of a double value. For finite nonzero |
|
|
//| numbers, the significand is in the range 1.0 ..< 2.0. |
|
|
//| An IEEE-754 double number = (Sign) 2 ^ Exponent * Significand. |
|
|
//+------------------------------------------------------------------+
|
|
double GetSignificand(const double value)
|
|
{
|
|
// return 1.0 + RawSignificand(value) / MathPow(2, 52);
|
|
return LongBitsToDouble(RawSignificand(value) | 0x3FF0000000000000);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Determine whether number is exactly representable in double. |
|
|
//| i.e., No rounding to an approximation during the conversion. |
|
|
//| Results are valid for numbers in the range [2^-24, 2^52]. |
|
|
//+------------------------------------------------------------------+
|
|
//https://stackoverflow.com/a/67952907/4208440
|
|
bool IsExactDouble(const double value)
|
|
{
|
|
if(value == 0)
|
|
return true;
|
|
|
|
int exp2 = GetExponent(value);
|
|
int exp10 = FloorLog10(value);
|
|
|
|
//--- check for any mismatch between the exact decimal and
|
|
//--- the round-trip representation.
|
|
int rightmost_bits = (52 - exp2) - (16 - exp10);
|
|
|
|
//--- create bitmask for rightmost bits
|
|
ulong mask = ((ulong)1 << rightmost_bits) - 1;
|
|
|
|
//--- test if all rightmost bits are 0's (i.e., no rounding)
|
|
return (DoubleToLongBits(value) & mask) == 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the next representable value after x away from zero. |
|
|
//+------------------------------------------------------------------+
|
|
double NextAfter(const double value)
|
|
{
|
|
long bits = DoubleToLongBits(value);
|
|
|
|
//--- Increment the integer representation to move to
|
|
//--- the next representable value away from zero.
|
|
return LongBitsToDouble(bits + 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Advances a floating-point number by a specified number of ULPs. |
|
|
//+------------------------------------------------------------------+
|
|
double DoubleAdvance(const double value, const long distance)
|
|
{
|
|
long bits = DoubleToLongBits(value);
|
|
|
|
return LongBitsToDouble(bits + distance);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the size of a unit in the last place (machine epsilon) |
|
|
//| for a specified double value. This is the unit of the least |
|
|
//| significant digit in this value’s significand. For most numbers, |
|
|
//| this is the positive distance between this double value and the |
|
|
//| representable value next larger in magnitude. |
|
|
//| Note that, Ulp(1.0) == DBL_EPSILON. (i.e., epsilon at 1.0) |
|
|
//+------------------------------------------------------------------+
|
|
double Ulp(const double value)
|
|
{
|
|
return MathAbs(NextAfter(value) - value);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the distance between two doubles a and b expressed as |
|
|
//| the number of gaps/bits/ULP (Units in the Last Place) between a |
|
|
//| and b. Note that the function returns a signed value indicating |
|
|
//| whether a > b or not. |
|
|
//+------------------------------------------------------------------+
|
|
long UlpDiff(const double a, const double b)
|
|
{
|
|
long bits1 = DoubleToLongBits(a);
|
|
long bits2 = DoubleToLongBits(b);
|
|
|
|
return bits1 - bits2;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| V. Handy functions for formatting of doubles to string. |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Converting numeric value into the raw hexadecimal text string. |
|
|
//| https://baseconvert.com/ieee-754-floating-point |
|
|
//+------------------------------------------------------------------+
|
|
string DoubleToHexadecimal(const double value)
|
|
{
|
|
long bits = DoubleToLongBits(value);
|
|
|
|
return StringFormat("0x%.16I64X", bits);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converting numeric value into the hex float constant string. |
|
|
//| A real number in format [−]0xh.hhhh P±dd, where h.hhhh – mantissa|
|
|
//| in the form of hexadecimal digits, using "ABCDEF", dd - One or |
|
|
//| more digits of exponent. |
|
|
//+------------------------------------------------------------------+
|
|
string DoubleToHexFloatConstant(const double value)
|
|
{
|
|
return StringFormat("%.13a", value);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converting numeric value into the exact decimal text string of |
|
|
//| the binary approximation stored by the machine. |
|
|
//| https://baseconvert.com/ieee-754-floating-point |
|
|
//+------------------------------------------------------------------+
|
|
string DoubleToStringExact(const double value)
|
|
{
|
|
if(!value || !MathIsValidNumber(value))
|
|
return (string)value;
|
|
|
|
//--- maximum number of decimal places = number of fraction bits
|
|
int exponent = (int) Floor(MathLog(MathAbs(value)) / M_LN2);
|
|
int digits = 52 - exponent;
|
|
|
|
//--- https://www.exploringbinary.com/number-of-decimal-digits-in-a-binary-fraction/
|
|
#ifdef __MQL4__
|
|
return StringFormat("%." + (string)digits + "f", value);
|
|
#else
|
|
return StringFormat("%.*f", digits, value);
|
|
#endif
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converting numeric value into a string in scientific notation |
|
|
//| with one digit before the decimal point (e.g., 6.22e-23). |
|
|
//| Digits : Optional. The number of digits after the decimal point. |
|
|
//| Defaults to as many digits as necessary to represent the value. |
|
|
//+------------------------------------------------------------------+
|
|
string DoubleToExponential(const double value, int digits = -1)
|
|
{
|
|
if(!value || !MathIsValidNumber(value) || digits > 20)
|
|
return (string)value;
|
|
|
|
//--- as many digits as necessary to represent the value
|
|
//--- https://www.w3schools.com/jsref/jsref_toexponential.asp
|
|
if(digits < 0)
|
|
digits = GetSignificantDigits(value) - 1;
|
|
//---
|
|
#ifdef __MQL4__
|
|
return StringFormat("%." + (string)digits + "e", value);
|
|
#else
|
|
return StringFormat("%.*e", digits, value);
|
|
#endif
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converting numeric value into the shortest string representation |
|
|
//| that round-trips into the same numeric value. The result will |
|
|
//| contain at most 17 significant digits, discarding trailing zeros.|
|
|
//| The round-trip ("%.17g") format specifier ensures that a numeric |
|
|
//| value converted to a string is always parsed back into the same |
|
|
//| numeric value, StringToDouble(Repr(f)) == f. |
|
|
//| Note: results are consistent with David M. Gay's dtoa.c library. |
|
|
//+------------------------------------------------------------------+
|
|
// toString()
|
|
string Repr(const double value)
|
|
{
|
|
//--- https://stackoverflow.com/a/35708911/4208440
|
|
//--- https://www.exploringbinary.com/number-of-digits-required-for-round-trip-conversions/
|
|
//--- Try format with 15, 16, 17 significant digits to return the shortest
|
|
//--- decimal numeric string which round-trips to the specified value.
|
|
string str = NULL;
|
|
for(int sig = 15; sig <= 17; sig++)
|
|
{
|
|
#ifdef __MQL4__
|
|
if(value == StringToDouble(str = StringFormat("%." + (string)sig + "g", value)))
|
|
break;
|
|
#else
|
|
if(value == StringToDouble(str = StringFormat("%.*g", sig, value)))
|
|
break;
|
|
#endif
|
|
}
|
|
return str;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converting float value into the shortest string representation |
|
|
//| that round-trips into the same numeric value. This ensures that |
|
|
//| a float value converted to a string is always parsed back into |
|
|
//| the same numeric value. i.e., (float)(Repr(f)) == f. |
|
|
//+------------------------------------------------------------------+
|
|
// toString()
|
|
string Repr(const float value)
|
|
{
|
|
//--- Try format with 6, 7, 8, 9 significant digits to return the shortest
|
|
//--- decimal numeric string which round-trips to the specified value.
|
|
string str = NULL;
|
|
for(int sig = 6; sig <= 9; sig++)
|
|
{
|
|
#ifdef __MQL4__
|
|
if(value == float(str = StringFormat("%." + (string)sig + "g", value)))
|
|
break;
|
|
#else
|
|
if(value == float(str = StringFormat("%.*g", sig, value)))
|
|
break;
|
|
#endif
|
|
}
|
|
return str;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Formats double with thousands separator and specified decimals. |
|
|
//+------------------------------------------------------------------+
|
|
/**
|
|
* Alert( FormatDouble(balance, 2) );
|
|
*/
|
|
string FormatDouble(const double value, const int digits, const string separator=",")
|
|
{
|
|
double numb = MathAbs(value);
|
|
string sign = (value < 0) ? "-" : "";
|
|
string num_str = DoubleToString(NormalizeDouble(numb, digits), digits); // FormatDouble(1.005, 2) => "1.01"
|
|
|
|
//--- Find the length of the integer part
|
|
int pos = StringFind(num_str, ".");
|
|
int length = (pos > -1) ? pos : StringLen(num_str);
|
|
|
|
//--- Format the integer part with thousand separators
|
|
for(int i = length - 3; i > 0; i -= 3)
|
|
{
|
|
// insert a separator at position i
|
|
string tmp = StringSubstr(num_str, 0, i);
|
|
tmp += separator;
|
|
num_str = tmp + StringSubstr(num_str, i);
|
|
}
|
|
|
|
return sign + num_str;
|
|
}
|
|
|
|
|
|
#endif // #ifndef MATH_UTILS_UNIQUE_HEADER_ID_H
|