Speed of execution of the functions ceil(),round(),floor() - page 4

 
Alexey Navoykov:

So DBL_EPSILON is 16 decimal places:2.2204460492503131e-016

And in your case you actually get one, as the difference is only 1e-16, which is 2 times less than epsilon.


It works with 0.99999999999999999997, but with 0.999999999999999998 it does not work anymore.

 
Nikolai Semko:

Works with 0.99999999999999999997, but with 0.999999999999999998 it no longer works.


Here's another trick:

Y=(int)(x+1-2*DBL_EPSILON);
Print(Y);                             // 3
Y=(int)(x+1-DBL_EPSILON-DBL_EPSILON);
Print(Y);                             // 4
 

The number 2.2204460492502503131e-016 in decimal notation means the resolution of the format double (https://msdn.microsoft.com/ru-ru/library/6bs3y5ya(v=vs.100).aspx), this

The smallest positive number x, such that x + 1.0 is not equal to 1.0.

- is the smallest positive number such that adding 1.0 to it changes the value of the number. In other words, it is the relative error of the representation of a number. That is, for 16 this boundary will contain so many decimal nines, and for 64 at the end it will be 4 times as much as 1, and for 8 half as much. So we will have to calculate this precision in order to speed up rounding with the highest possible precision. It is unlikely that these calculations will be faster than the performance of regular rounding functions. Since MQL does not allow working directly with the addresses of numbers, we will not be able to take bits of an order in the number, so we will have to invent the analog of the log function in base 2. It is unlikely to work fast.

Пределы констант с плавающей запятой
Пределы констант с плавающей запятой
  • msdn.microsoft.com
В следующей таблице представлены ограничения на значения констант с плавающей запятой. Эти ограничения также заданы в стандартном файле заголовка FLOAT.H. Ограничения на константы с плавающей запятой КонстантаЗначениеЗначение Количество цифр q, при котором число с плавающей запятой с q десятичными цифрами можно округлить в представление с...
 
Nikolai Semko:

Here's another trick:

It turns out that any mathematical operation introduces an additional error, which can be either plus or minus. Therefore, in general, as Vladimir pointed out, it is necessary to normalise the result.

 

ceil(), floor() are unlikely to be replicated because the FPU rounding mode cannot be controlled from μl. The only thing that will probably work more or less adequately is round() and it is not exact according to the standard. By the way, this is roughly what C-shaped round() looks like:

double round(double x)
{
    fenv_t save_env;
    feholdexcept(&save_env);
    double result = rint(x);
    if (fetestexcept(FE_INEXACT)) {
        fesetround(FE_TOWARDZERO);
        result = rint(copysign(0.5 + fabs(x), x));
    }
    feupdateenv(&save_env);
    return result;
}
 
To understand what you want to round here, you need to know exactly how and what is lost when converting DOUBLE format to INT format. in this case, you only get the integer part, which remains after rounding. as a consequence, any number with any number of decimal places and any digits after the decimal point will be removed and only the integer part remains.
example:
2.1758716871 -> (int)2.1758716871 = 2
2.0000000001 -> (int)2.0000000001 = 2
2.9999999999 -> (int)2.9999999999 = 2
this is called loss of precision when a number is reformatted.
as a consequence
MathFloor(x)  =  (int)(x)
MathCeil(x)   =  (int)(x)+1;
MathRound(x)  =  (int)(x+0.5)

Sincerely.
 
Andrey Kisselyov:
To understand what you want to round here, you need to know exactly how and what is lost when converting DOUBLE format to INT format. in this case, you only get the integer part of it, which remains after truncation. as a consequence, any number with any number of decimal places and any digits after the comma will be removed and only the integer part remains.
example:This is called loss of precision when a number is reformatted.
as a consequence

Sincerely.

Why is it so complicated?

MathRound(x)  - >  (int)(x)+(int)(2.*(x-(int)(x)));

How is this better than the simpler one?

MathRound(x)  ->  (int)(x+0.5);
 
Alexey Navoykov:

It turns out that any mathematical operation introduces an additional error, which can be either plus or minus. Therefore, in the general case, as pointed out by Vladimir, it is necessary to normalize the result.


pavlick_:

ceil(), floor() are unlikely to be replicated, as FPU rounding mode cannot be controlled from μl. The only thing that will probably work more or less adequately is round() and it is not exact as per the standard. By the way, this is approximately what C-shaped round() looks like:


Of course, you're right. But I'll repeat. For 99.9% of tasks it is absolutely correct to replace rounding functions with alternative faster variant using conversion of fractional types to (int) or (long). An experienced programmer just has to be aware of this fact and apply it when needed.

Для положительных x:
y=floor(x); -> y=(int)x;
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
y=round(x); -> y=(int)(x+0.5);
 
Nikolai Semko:

Why is it so complicated?

How is this better than the simpler one?

you can try it that way.

Nikolai Semko:
An experienced programmer just has to be aware of this fact and apply it when expedient.

It's not that he must, he must know it.

With respect.

P.S. Your formula is not correct for the entire set of values.
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
see mine, I updated round to a short formula, now the list is complete and fully functional, use it.
 
Andrey Kisselyov:


P.S. your formula is not true for the whole set of values.

see mine, I updated round to a short formula, now the list is complete and fully functional, use it.

see above

when x = 3 (any integer), there is a jam :))