Velocità di esecuzione delle funzioni ceil(),round(),floor() - pagina 4

 
Alexey Navoykov:

Quindi DBL_EPSILON è 16 cifre decimali:2.2204460492503131e-016

E nel tuo caso ne ottieni effettivamente uno, dato che la differenza è solo 1e-16, che è 2 volte meno di epsilon.


Funziona con 0,999999999999999999999997, ma con 0,999999999999999998 non funziona più.

 
Nikolai Semko:

Funziona con 0,9999999999999999999997, ma con 0,999999999999999998 non funziona più.


Ecco un altro trucco:

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

Il numero 2.2204460492502503131e-016 in notazione decimale significa la risoluzione del formato doppio (https://msdn.microsoft.com/ru-ru/library/6bs3y5ya(v=vs.100).aspx), questo

Il più piccolo numero positivo x, tale che x + 1,0 non è uguale a 1,0.

- Il più piccolo numero positivo tale che l'aggiunta di 1,0 cambia il valore del numero. In altre parole, è l'errore relativo della rappresentazione di un numero. Cioè, per 16 questo confine conterrà tanti nove decimali, e per 64 alla fine sarà 4 volte tanto 1, per 8 la metà. Quindi dovremo calcolare questa precisione per accelerare l'arrotondamento con la massima precisione possibile. È improbabile che questi calcoli siano più veloci delle prestazioni delle funzioni di arrotondamento regolari. Poiché MQL non permette di lavorare direttamente con indirizzi di numeri, non potremo prendere bit di un ordine nel numero, quindi dovremo inventare l'analogo della funzione log in base 2. È improbabile che funzioni velocemente.

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

Ecco un'altra stranezza:

Si scopre che qualsiasi operazione matematica introduce un errore aggiuntivo, che può essere più o meno. Quindi, in generale, come ha sottolineato Vladimir, è necessario normalizzare il risultato.

 

ceil(), floor() è improbabile che siano replicati perché la modalità di arrotondamento della FPU non può essere controllata dal µl. L'unica cosa che probabilmente funzionerà più o meno adeguatamente è round() e non è esatta secondo lo standard. A proposito, questo è più o meno l'aspetto di Round() a forma di 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;
}
 
Per capire cosa si vuole arrotondare qui, è necessario sapere esattamente come e cosa si perde quando si converte il formato DOUBLE nel formato INT. in questo caso, si ottiene solo la parte intera, che rimane dopo l'arrotondamento. di conseguenza, qualsiasi numero con qualsiasi numero di cifre decimali e qualsiasi cifra dopo la virgola verrà rimossa e rimarrà solo la parte intera.
esempio:
2.1758716871 -> (int)2.1758716871 = 2
2.0000000001 -> (int)2.0000000001 = 2
2.9999999999 -> (int)2.9999999999 = 2
Questa si chiama perdita di precisione quando un numero viene riformattato.
come conseguenza
MathFloor(x)  =  (int)(x)
MathCeil(x)   =  (int)(x)+1;
MathRound(x)  =  (int)(x+0.5)

Sinceramente.
 
Andrey Kisselyov:
Per capire cosa si vuole arrotondare qui, è necessario sapere esattamente come e cosa si perde quando si converte il formato DOUBLE in formato INT. in questo caso, si ottiene solo la parte intera, che rimane dopo il troncamento. di conseguenza, qualsiasi numero con qualsiasi numero di cifre decimali e qualsiasi cifra dopo la virgola sarà rimossa e rimane solo la parte intera.
esempio:Questo si chiama perdita di precisione quando un numero viene riformattato.
come conseguenza

Sinceramente.

Perché è così complicato?

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

In che modo questo è meglio di quello più semplice?

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

Si scopre che qualsiasi operazione matematica introduce un errore aggiuntivo, che può essere più o meno. Pertanto, nel caso generale, come sottolineato da Vladimir, è necessario normalizzare il risultato.


pavlick_:

ceil(), floor() è improbabile che siano replicati, poiché la modalità di arrotondamento della FPU non può essere controllata da μl. L'unica cosa che probabilmente funzionerà più o meno adeguatamente è round() e non è esatto come da standard. A proposito, questo è all'incirca l'aspetto di Round() a forma di C:


Naturalmente, hai ragione. Ma lo ripeto. Per il 99,9% dei compiti è assolutamente corretto sostituire le funzioni di arrotondamento con una variante alternativa più veloce usando la conversione dei tipi frazionari in (int) o (long). Un programmatore esperto deve solo essere consapevole di questo fatto e applicarlo quando necessario.

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

Perché è così complicato?

In che modo questo è meglio di quello più semplice?

si può provare in questo modo.

Nikolai Semko:
Un programmatore esperto deve solo essere consapevole di questo fatto e applicarlo quando è opportuno.

Non è che deve, deve saperlo.

Con rispetto.

P.S. La tua formula non è corretta per l'intera serie di valori.
y=ceil(x);  -> y=(int)(x+0.9999999999999997);
vedere il mio, ho aggiornato il giro ad una formula breve, ora l'elenco è completo e completamente funzionale, usatelo.
 
Andrey Kisselyov:


P.S. la tua formula non è vera per tutto l'insieme dei valori.

vedere il mio, ho aggiornato il giro ad una formula breve, ora l'elenco è completo e completamente funzionale, usatelo.

vedi sopra

quando x = 3 (qualsiasi numero intero), c'è un inceppamento :))