ERROR in MQL NormalizeDouble() - page 2

Sardion Maranatha  
torytory:

NormalizeDouble

Rounding floating point number to a specified accuracy.

double  NormalizeDouble(
   double  value,      // normalized number
   int     digits      // number of digits after decimal point
   );

When rounding the float point number with a specified accuracy of 2 digits results in abnormal behavior 

i did a little try, not sure will help but hopefully it will do, sent to your inbox
Flohti  

Hi everybody,


I've notice the same thing, and I also wanted MathCeil and MathFloor for double numbers, I've redesigned these functions and MathRound in my code for that :

double MathRound (double value, int digit) {
    return MathRound (value * MathPow (10, digit)) / MathPow (10, digit);
}

double MathFloor (double value, int digit) {
    return MathFloor (value * MathPow (10, digit)) / MathPow (10, digit);
}

double MathCeil (double value, int digit) {
    return MathCeil (value * MathPow (10, digit)) / MathPow (10, digit);
}

That works very well, with the price of at least twice the time of NormalizeDouble execution


If this can help someone

amrali  
Flohti #:

That works very well, with the price of at least twice the time of NormalizeDouble execution


If this can help someone

This works very well most of the time,  but not all the time. 
For example: 
MathRound(1.005, 2) returns wrong 1.00
MathFloor(2.26, 2) returns wrong 2.25
MathCeil(9.13, 2) returns wrong 9.14

Flohti  

Exact, thanks,

I just tested your examples with a NormalizeDouble just before the function on integer like that :

double MathRound (double value, int digit) {
    return MathRound (NormalizeDouble (value * MathPow (10, digit), 0)) / MathPow (10, digit);
}

double MathFloor (double value, int digit) {
    return MathFloor (NormalizeDouble (value * MathPow (10, digit), 0)) / MathPow (10, digit);
}

double MathCeil (double value, int digit) {
    return MathCeil (NormalizeDouble (value * MathPow (10, digit), 0)) / MathPow (10, digit);
}

the time for this plus is insignificant

It seems ok, what do you think ?

amrali  
Flohti #:

Exact, thanks,

I just tested your examples with a NormalizeDouble just before the function on integer like that :

the time for this plus is insignificant

It seems ok, what do you think ?

No, unfortunately. For example: MathFloor(2.157, 2) => 2.16 which is wrong, it should be 2.15.

Please see my library for precise functions for rounding. https://www.mql5.com/en/code/20822

(Actually, these are not mandatory for rounding prices for trading orders, but simple functions are Ok).

I am sending you a script to compare how your functions perform against Microsoft .Net library (the most precise for decimal rounding, as it uses the 128-bit decimal type).

Edit: I re-uploaded the files due to some mistakes in the code. Also, please install "Ron's CSV Edit" on your computer to open files\result.csv automatically.
Math Utils
Math Utils
  • www.mql5.com
Handy functions for comparison, rounding, formatting and debugging of doubles (prices, lots and money).
Flohti  

Ok, I've seen your math utils library and tried your function with mathlog10 etc in a loop, and tried too with just DBL_DIG for my NormalizeDouble digit,

I think mine with DBL_DIG is more than enough, 13ms for some 1 million loop with some mathrand, mathround, conditions, comparisons, it's twice the time earlier,

The same loop with yours take 56ms, I do a so complex EA with a lot of calculations, I don't do any round if it's not necessary but I can't accept this difference,

I do NormalyzeDouble the value * 10power at DBL_DIG,  and added or substract FLT_EPSILON, like I do for comparisons, for compute the MathFunction correctly and then divide by 10power and seems ok with all of your examples and other like floor 2.149999 etc

Thank you a lot for your contribution, It has helped me so well to do something the most exact like possible.. Look like this now : (two MathPow don't take any longer than int in RAM, I tried DBL_EPSILON, that does not work)

double MathRound (double value, int digits) {
    return MathRound (NormalizeDouble (value * MathPow (10, digits), DBL_DIG)) / MathPow (10, digits);
}

double MathFloor (double value, int digits) {
    return MathFloor (NormalizeDouble (value * MathPow (10, digits) + FLT_EPSILON, DBL_DIG)) / MathPow (10, digits);
}

double MathCeil (double value, int digits) {
    return MathCeil (NormalizeDouble (value * MathPow (10, digits) - FLT_EPSILON, DBL_DIG)) / MathPow (10, digits);
}

int DoubleCompare (double value, double compare) {
        if (value + FLT_EPSILON < compare)
                return -1;
        if (value - FLT_EPSILON > compare)
                return 1;
        return 0;
}
amrali  

If you look for speed + accuracy:

double MathRound (double value, int digits) { double p = MathPow(10, digits); return MathRound((value * p) * (1 + DBL_EPSILON)) / p; }
double MathFloor (double value, int digits) { double p = MathPow(10, digits); return MathFloor((value * p) * (1 + DBL_EPSILON)) / p; }
double MathCeil (double value, int digits ) { double p = MathPow(10, digits); return MathCeil ((value * p) * (1 - DBL_EPSILON)) / p; }

But, this works for POSITIVE numbers, only.

Flohti  
Nice, very nice, I can do exact the same thing as My compare function, just add or substract FLT_EBSILON seems working and so fast, thank you, why don't have I thought that..
double MathRound (double value, int digits) {
        return MathRound(value * MathPow(10, digits) + FLT_EPSILON) / MathPow(10, digits);
}

double MathFloor (double value, int digits) {
        return MathFloor(value * MathPow(10, digits) + FLT_EPSILON) / MathPow(10, digits);
}

double MathCeil (double value, int digits ) {
        return MathCeil (value * MathPow(10, digits) - FLT_EPSILON) / MathPow(10, digits);
}

I thing your multiple 1 plus DBL_EPSILON is better for accuracy with higher digits than 6, but i don't need that

I do a simple little loop to extract the digit integer value of volume step, maybe there is a better way, I don't have thought about that for a time

Flohti  
amrali #:

If you look for speed + accuracy:

But, this works for POSITIVE numbers, only.

Add a little condition

double epsilon = value < DBL_EPSILON ? - DBL_EPSILON : DBL_EPSILON;
double epsilon = value < FLT_EPSILON ? - FLT_EPSILON : FLT_EPSILON;

This does not add much more time

I've seen your deleted answer where you mentioned MathAbs(MathLog10(step)), thanks, my math is old, I did not remember what was log10

amrali  

Nice. Thanks!

Now, it works with positive or negative numbers. I used the RoundSharp .Net library to verify its correctness.

I have saved it to my library like that:

double MathRound(double value, int digits) { double p = MathPow(10, digits); return MathRound((value * p) * (1 + DBL_EPSILON)) / p; }
double MathFloor(double value, int digits) { double p = MathPow(10, digits); return MathFloor((value * p) * (value > 0 ? 1 + DBL_EPSILON : 1 - DBL_EPSILON)) / p; }
double MathCeil (double value, int digits) { double p = MathPow(10, digits); return MathCeil ((value * p) * (value > 0 ? 1 - DBL_EPSILON : 1 + DBL_EPSILON)) / p; }
double MathTrunc(double value, int digits) { return (value >= 0) ? MathFloor(value, digits) : MathCeil(value, digits); }

Note, use negative digits to round to tens and hundreds.  MathRound(1327, -2) => 1300

Edit: updated code + attached newer verification script.