sintaxis - sqrt c++ español
c++ sqrt precisión garantizada, límite superior/inferior (3)
Tengo que comprobar una desigualdad que contiene raíces cuadradas. Para evitar resultados incorrectos debido a la imprecisión y redondeo de punto flotante, uso std::nextafter()
para obtener un límite superior / inferior:
#include <cfloat> // DBL_MAX
#include <cmath> // std::nextafter, std::sqrt
double x = 42.0; //just an example number
double y = std::nextafter(std::sqrt(x), DBL_MAX);
a) ¿Se garantiza y*y >= x
utilizando el compilador GCC?
b) ¿Funcionará para otras operaciones como + - * /
o incluso std::cos()
y std::acos()
?
c) ¿Hay mejores maneras de obtener límites superiores / inferiores?
Actualización: He read esto no está garantizado por el estándar de C ++, pero debería funcionar de acuerdo con IEEE-754. ¿Funcionará esto con el compilador GCC?
En general, las operaciones de punto flotante darán como resultado algún error de ULP . IEEE 754 requiere que los resultados para la mayoría de las operaciones sean correctos dentro de 0.5 ULP, pero los errores pueden acumularse, lo que significa que un resultado puede no estar dentro de un ULP del resultado exacto. También hay límites de precisión, por lo que, dependiendo del número de dígitos que haya en los valores resultantes, es posible que tampoco esté trabajando con valores de las mismas magnitudes. Las funciones trascendentales también son algo notorious para introducir errores en los cálculos.
Sin embargo, si está utilizando GNU glibc , sqrt será correcto dentro de 0.5 ULP (redondeado), por lo que su ejemplo específico funcionaría (descuidando NaN
, +/-0
, +/-Inf
). Aunque, probablemente es mejor definir algunos épsilon como su tolerancia al error y usarlos como su límite. Por ejemplo,
bool gt(double a, double b, double eps) {
return (a > b - eps);
}
Dependiendo del nivel de precisión que necesite en los cálculos, también puede usar el long double
lugar.
Por lo tanto, para responder a sus preguntas ...
a) ¿Se garantiza y * y> = x utilizando el compilador GCC?
Suponiendo que utilice GNU glibc o SSE2 intrinsics, sí.
b) ¿Funcionará para otras operaciones como + - * / o incluso std :: cos () y std :: acos ()?
Suponiendo que utilice GNU glibc y una operación, sí. Aunque algunos trascendentales no están garantizados correctamente redondeados.
c) ¿Hay mejores maneras de obtener límites superiores / inferiores?
Debe saber cuál es su tolerancia a errores en los cálculos y usarla como épsilon (que puede ser más grande que un ULP).
Para GCC, this página sugiere que funcionará si utiliza la función sqrt incorporada de GCC __builtin_sqrt
.
Además, este comportamiento dependerá de cómo compile su código y la máquina en la que se ejecuta
Si el procesador admite SSE2, debe compilar su código con los indicadores
-mfpmath=sse -msse2
para asegurarse de que todas las operaciones de punto flotante se realicen utilizando los registros de SSE.Si el procesador no admite SSE2, debe usar el tipo
long double
para los valores de punto flotante y compilar con la bandera-ffloat-store
para obligar a GCC a no usar registros para almacenar valores de punto flotante (tendrá una penalización de rendimiento). por hacer esto)
Sobre
c) ¿Hay mejores maneras de obtener límites superiores / inferiores?
Otra forma es usar un modo de redondeo diferente, es decir, FE_UPWARD
o FE_DOWNWARD
lugar del FE_TONEAREST
predeterminado. Consulte https://.com/a/6867722 Esto puede ser slower , pero es un mejor límite superior / inferior.