que proceso parametros funciona con compilar compilador compilacion como comando archivo c++ linux gcc assembly floating-point

c++ - proceso - ¿Es esta optimización GCC incorrecta?



proceso de compilacion gcc (4)

No debería preocuparse por la salida diferente. Si lanzas un valor flotante a un doble y luego vuelves a un valor flotante; puedes obtener un valor diferente; por lo tanto, el diferente valor que obtiene al convertir un flotante en un valor doble, realizar la operación de registro y luego devolver el resultado a un flotante no es mejor que el valor que obtiene llamando directamente a la función logf.

Si le preocupa la precisión de la operación, entonces no debe usar el tipo de letra flotante y todos sus valores matemáticos deben ser el doble hasta el final.

Al usar el tipo de flotación, indicas al compilador que no tienes especial atención en la precisión final de tus operaciones matemáticas más allá de la proporcionada por el tipo de flotación. En otras palabras, cuando realizas algunas operaciones matemáticas con números reales (números distintos de enteros), la precisión solo puede permanecer igual o disminuir; nunca puede aumentar; por lo tanto, si comienza con un tipo de letra flotante, no puede aumentar su precisión más allá de ese tipo.

Tengo un functor que toma un valor, lo dobla, toma el registro y devuelve el valor al tipo original. A los efectos de esta pregunta, el tipo original y de salida es flotante. Aquí está el código original de C ++:

return static_cast< TOutput >( std::log( static_cast< double >( A ) ) )

Cuando compilo en modo de depuración, todo va según lo esperado y GCC llama a la función de log subyacente:

51:/myfile.h **** return static_cast< TOutput >( std::log( static_cast< double >( A ) ) ); 219133 .loc 112 51 0 219134 0010 488B45F0 movq -16(%rbp), %rax # A, tmp64 219135 0014 F30F1000 movss (%rax), %xmm0 # *A_1(D), D.237346 219136 0018 0F14C0 unpcklps %xmm0, %xmm0 # D.237346, D.237346 219137 001b 0F5AC0 cvtps2pd %xmm0, %xmm0 # D.237346, D.237347 219138 001e E8000000 call log # 219138 00 219139 0023 660F14C0 unpcklpd %xmm0, %xmm0 # D.237347 219140 0027 660F5AC0 cvtpd2ps %xmm0, %xmm0 # D.237347, D.237346 219141 002b F30F1145 movss %xmm0, -20(%rbp) # D.237346, %sfp 219141 EC 219142 0030 8B45EC movl -20(%rbp), %eax # %sfp, <retval>

Sin embargo, cuando activo las optimizaciones (-O2 -ggdb3 -DNDEBUG), llama a la función logf (???):

51:/myfile.h **** return static_cast< TOutput >( std::log( static_cast< double >( A ) ) ); 145171 .loc 64 51 0 145172 01a0 F30F1004 movss (%rdx,%rax,4), %xmm0 # MEM[(const float &)_84], MEM[(const float &)_84] 145172 82 145173 01a5 E8000000 call logf #

Da una salida diferente. ¿Esto es normal? ¿Estoy haciendo algo mal? Me parece que GCC está tomando una interpretación muy liberal de mi código, lo cual no esperaría en ausencia de la opción -ffast-math .


Probé un programa C equivalente:

#include <math.h> #include <stdlib.h> #include <stdio.h> static double dostuff(float in) { return log(in); } int main(int argc, char **argv) { if (argc < 2) exit(EXIT_FAILURE); float in = strtof(argv[1], NULL); float out = dostuff(in); printf("%f/n", out); return 0; }

Y descubrió que gcc no usa logf incluso cuando usa -Ofast . Lo único -Ofast habilita es __log_finite() para ser utilizado. Sin embargo, cambiar el tipo de dostuff() de dostuff() para float permite esta optimización.


Sí, esta optimización es incorrecta. log y logf solo deben cumplirse fielmente, por lo que uno podría tener

logf(4) = 0x1.62e42p+0 log(4) = 0x1.62e42fefa39efp+0

Cambiar la conversión ascendente, el log y la conversión descendente a una llamada de logf puede dar resultados incorrectos.

La respuesta de Pascal Cuoq señala correctamente que, si logf está redondeado correctamente y el log no es basura, entonces los resultados probablemente no diferirán. Sin embargo, logf en mi plataforma no está redondeado correctamente:

logf(0x1.306p-138) = -0x1.7decc8p+6 (float)log(0x1.306p-138) = -0x1.7decc6p+6 mpfr_log(0x1.306p-138) = -0x1.7decc6ff8a7a4a4450e9p+6

Afortunadamente, no puedo reproducir esta "optimización" con mi gcc.


Se trata de una optimización límite para transformar la conversión a float de una aplicación del log precisión doble en flotante a una aplicación del log precisión simple, pero puede argumentarse que es aceptable.

Suponiendo que logf se redondea correctamente y que el log precisión doble también se redondea correctamente o al menos se redondea fielmente, los dos cálculos rara vez serán diferentes. Pueden diferir (para algunas entradas raras) debido al double-rounding (en el que "doble" significa "dos veces" y no se refiere al tipo). Doble redondeo es estadísticamente menos significativo que hay dígitos adicionales en el significado del tipo intermedio en comparación con el significado del tipo final (y este argumento estadístico es un poco basura desde un punto de vista matemático, pero "funciona en la práctica" para funciones que no han sido diseñados para ser contraejemplos). Por razones didácticas, las personas (o Wikipedia) lo explican con uno o dos dígitos extra de precisión, pero cuando tienes 53 - 24 = 29 dígitos binarios adicionales, se puede esperar que ocurra tan raramente como una vez en 2 29 .

Estoy sorprendido por la optimización, y me molestaría si hubiera escrito el código para una búsqueda exhaustiva de problemas de doble redondeo con el log , pero teniendo en cuenta que el estándar C ++ no exige ningún nivel de precisión de std::log , es posible considerarlo "no un error".

Si, en lugar de log , hubiéramos estado hablando de una de las operaciones básicas (como * ), entonces la transformación habría sido incorrecta, para un compilador que dice proporcionar semántica IEEE 754, cuando introduce cambios visibles. Para una operación básica, la precisión se especifica indirectamente por IEEE 754, y la especificación no deja lugar a variaciones.

Ocurre que para las operaciones básicas, no puede haber un cambio visible cuando floatOP(flx,fly) reemplaza (float)doubleOP((double)flx, (double)fly) (esta thesis demuestra en el capítulo 6), pero hay pueden ser diferencias visibles cuando los tipos son double y long double . Este error exacto fue corregido recientemente por Stephen Canon en Clang.