Velocidade de execução das funções ceil(),round(),floor() - página 4

 
Alexey Navoykov:

Então DBL_EPSILON tem 16 casas decimais:2,2204460492503131e-016

E no seu caso você realmente recebe um, pois a diferença é apenas 1e-16, o que é 2 vezes menor que o epsilon.


Funciona com 0,999999999999999999999997, mas com 0,99999999999999999998 não funciona mais.

 
Nikolai Semko:

Funciona com 0,999999999999999999999997, mas com 0,99999999999999999998 não funciona mais.


Aqui está outro truque:

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

O número 2.2204460492502502503131e-016 em notação decimal significa a resolução do formato em dobro (https://msdn.microsoft.com/ru-ru/library/6bs3y5ya(v=vs.100).aspx), este

O menor número positivo x, de tal forma que x + 1,0 não é igual a 1,0.

- é o menor número positivo de tal forma que adicionar 1,0 a ele altera o valor do número. Em outras palavras, é o erro relativo da representação de um número. Ou seja, para 16 este limite conterá tantas noves decimais, e para 64 no final será 4 vezes mais do que 1, e para 8 vezes mais da metade. Portanto, teremos que calcular esta precisão para acelerar o arredondamento com a maior precisão possível. É improvável que estes cálculos sejam mais rápidos do que o desempenho das funções de arredondamento regulares. Uma vez que a MQL não permite trabalhar diretamente com os endereços dos números, não poderemos tomar bits de uma ordem no número, então teremos que inventar o análogo da função de registro na base 2. É pouco provável que funcione rapidamente.

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

Aqui está outro truque:

Acontece que qualquer operação matemática introduz um erro adicional, que pode ser ou mais ou menos. Portanto, em geral, como Vladimir assinalou, é necessário normalizar o resultado.

 

ceil(), floor() são improváveis de serem replicados porque o modo de arredondamento da FPU não pode ser controlado a partir do µl. A única coisa que provavelmente funcionará mais ou menos adequadamente é redonda() e não é exata de acordo com o padrão. A propósito, esta é mais ou menos a forma redonda em forma de C():

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;
}
 
Para entender o que você quer arredondar aqui, você precisa saber exatamente como e o que se perde ao converter o formato DOUBLE para o formato INT. neste caso, você só recebe a parte inteira, que permanece após o arredondamento. como conseqüência, qualquer número com qualquer número de casas decimais e qualquer dígito após o ponto decimal será removido e somente a parte inteira permanecerá.
exemplo:
2.1758716871 -> (int)2.1758716871 = 2
2.0000000001 -> (int)2.0000000001 = 2
2.9999999999 -> (int)2.9999999999 = 2
Isto se chama perda de precisão quando um número é reformatado.
como conseqüência
MathFloor(x)  =  (int)(x)
MathCeil(x)   =  (int)(x)+1;
MathRound(x)  =  (int)(x+0.5)

Sinceramente.
 
Andrey Kisselyov:
Para entender o que você quer arredondar aqui, você precisa saber exatamente como e o que se perde ao converter o formato DOUBLE para o formato INT. neste caso, você só recebe a parte inteira, que permanece após o truncamento. como conseqüência, qualquer número com qualquer número de casas decimais e qualquer dígito após a vírgula será removido e somente a parte inteira permanece.
exemplo: Isto se chama perda de precisão quando um número é reformatado.
como conseqüência

Sinceramente.

Por que é tão complicado?

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

Como isso é melhor do que o mais simples?

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

Acontece que qualquer operação matemática introduz um erro adicional, que pode ser ou mais ou menos. Por isso, no caso geral, como foi apontado por Vladimir, é necessário normalizar o resultado.


pavlick_:

ceil(), floor() dificilmente serão replicados, pois o modo de arredondamento da FPU não pode ser controlado a partir de μl. A única coisa que provavelmente funcionará mais ou menos adequadamente é redonda() e não é exata de acordo com o padrão. A propósito, isto é aproximadamente o que parece a forma redonda em C():


Claro, você está certo. Mas vou repetir. Para 99,9% das tarefas é absolutamente correto substituir as funções de arredondamento por variantes alternativas e mais rápidas usando a conversão dos tipos fracionários para (int) ou (longo). Um programador experiente só tem que estar ciente deste fato e aplicá-lo quando necessário.

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

Por que é tão complicado?

Como isso é melhor do que o mais simples?

você pode tentar dessa forma.

Nikolai Semko:
Um programador experiente só tem que estar ciente deste fato e aplicá-lo quando for conveniente.

Não é que ele deva, ele deve saber disso.

Com todo respeito.

P.S. Sua fórmula não é correta para todo o conjunto de valores.
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
veja o meu, atualizei redondo para uma fórmula curta, agora a lista está completa e totalmente funcional, use-a.
 
Andrey Kisselyov:


P.S. sua fórmula não é verdadeira para todo o conjunto de valores.

veja o meu, atualizei redondo para uma fórmula curta, agora a lista está completa e totalmente funcional, use-a.

ver acima

quando x = 3 (qualquer número inteiro), há um engarrafamento :))