Скорость выполнения функций ceil(),round(),floor() - страница 4

 
Alexey Navoykov:

Так DBL_EPSILON - это и есть 16 знаков после запятой:  2.2204460492503131e-016

А в вашем случае получается фактически единица, т.к. разница составляет всего 1e-16, что в 2 раза меньше epsilon.


Работает с 0.9999999999999997, а с 0.9999999999999998 уже не работает. 

 
Nikolai Semko:

Работает с 0.9999999999999997, а с 0.9999999999999998 уже не работает. 


Вот еще прикол:

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

Число 2.2204460492503131e-016 в десятичной записи означает разрешающую способность формата double (https://msdn.microsoft.com/ru-ru/library/6bs3y5ya(v=vs.100).aspx), это 

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

- наименьшее положительное число, такое, что добавление к нему 1.0 изменяет значение числа. Иначе говоря, это относительная погрешность представления числа. То есть для 16 эта граница будет содержать  столько-то десятичных девяток, а для 64 в конце будет отличие от 1 в 4 раза больше, для 8 в два раза меньше. Так что для ускорения округления  с максимально возможной точностью придется эту точность вычислять. Вряд ли эти вычисления произойдут быстрее, чем исполнение штатных функций округления. Поскольку MQL не дает возможности работать прямо с адресами чисел, то брать в числе биты порядка не удастся, нужно будет придумывать аналог функции log по основанию 2. Она вряд ли будет работать быстро.

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

Вот еще прикол:

Получается, что любое математическое действие вносит дополнительную погрешность, которая может быть как в плюс, так и в минус.  Поэтому в общем случае, как подметил Vladimir, требуется нормализовывать полученный результат.

 

ceil(), floor() повторить вряд ли удастся, т.к. из мкл нельзя управлять режимом округления FPU. Единственное,  наверное, что будет работать более менее адекватно - round() и то не точно по стандарту. Кстати, примерно так выглядит Си-шная round():

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;
}
 
для понимания того что вы тут хотите округлять, нужно знать как именно и что теряется при переводе формата DOUBLE  в формат INT. в данном случае вы получаете только целую его часть, которая остается после урезания. как следствие любое число с любым количеством знаков после запятой и любыми цифрами после запятой будут удалены и останется только целая часть.
пример:
2.1758716871 -> (int)2.1758716871 = 2
2.0000000001 -> (int)2.0000000001 = 2
2.9999999999 -> (int)2.9999999999 = 2
это называется потеря точности при изменении формата числа.
как следствие
MathFloor(x)  =  (int)(x)
MathCeil(x)   =  (int)(x)+1;
MathRound(x)  =  (int)(x+0.5)

с уважением.
 
Andrey Kisselyov:
для понимания того что вы тут хотите округлять, нужно знать как именно и что теряется при переводе формата DOUBLE  в формат INT. в данном случае вы получаете только целую его часть, которая остается после урезания. как следствие любое число с любым количеством знаков после запятой и любыми цифрами после запятой будут удалены и останется только целая часть.
пример:это называется потеря точности при изменении формата числа.
как следствие

с уважением.

А зачем так сложно:

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

Чем такой вариант лучше более простого?:

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

Получается, что любое математическое действие вносит дополнительную погрешность, которая может быть как в плюс, так и в минус.  Поэтому в общем случае, как подметил Vladimir, требуется нормализовывать полученный результат.


pavlick_:

ceil(), floor() повторить вряд ли удастся, т.к. из мкл нельзя управлять режимом округления FPU. Единственное,  наверное, что будет работать более менее адекватно - round() и то не точно по стандарту. Кстати, примерно так выглядит Си-шная round():


Конечно вы правы. Но я повторюсь. Для 99.9% задач абсолютно правомерна замена округляющих функций альтернативным более быстрым вариантом с применением преобразования дробных типов на (int) или (long). Опытный программист просто должен осознавать этот факт и применять его при целесообразности.

Для положительных 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:

А зачем так сложно:

Чем такой вариант лучше более простого?:

можно и так попробовать.

Nikolai Semko:
Опытный программист просто должен осознавать этот факт и применять его при целесообразности.

он не то что должен, он обязан это знать.

с уважением.

P.S. ваша формула не верна для всего множества значений.
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
посмотрите мою, я обновил роунд до короткой формулы, теперь список полон и полностью работоспособен, пользуйтесь.
 
Andrey Kisselyov:


P.S. ваша формула не верна для всего множества значений.

посмотрите мою, я обновил роунд до короткой формулы, теперь список полон и полностью работоспособен, пользуйтесь.

см. выше

при x = 3 (любое целое число) происходит косячок :))

Причина обращения: