ejemplos - punteros y arreglos en c
¿Por qué un printf() permite que este doble pase por un puntero? (2)
¿Es este un efecto de optimización (defectuosa)?
Desactive cualquier optimización (¿DEPURAR?) Y vea si obtiene el mismo efecto.
Por supuesto, si encuentras que es el optimizador, entonces estás en el punto crítico y solo puedes hacer algo para engañar, por ejemplo, un sprintf que no hace nada.
Además, tu printf también podría imprimir el puntero ("% 16x", (long) & rho) no es que yo piense que es incorrecto, sino solo como una cláusula de cordura en caso de que nos falte el summat. Además, el resultado de la mayoría de dobles con bits aleatorios generalmente termina en el rango de E +/- 317, por lo que el resultado de 0.000171 es demasiado razonable como para ser completamente sospechoso.
Un par de instrucciones de depuración printf()
revela que un puntero a un doble que estoy pasando es, cuando se desreferencia en el extremo receptor, que sale como un valor diferente, pero solo bajo Microsoft Visual Studio (versión 9.0). Los pasos son bastante simples:
double rho=0; /* distance from the Earth */
/* ... */
for (pass = 0; pass < 2; pass++) {
/* ... */
rho = sqrt(rsn*rsn+rp*rp-2*rsn*rp*cpsi*cos(ll));
printf("/nrho from sqrt(): %f/n", rho);
/* ... */
}
/* ... */
cir_sky (np, lpd, psi, rp, &rho, lam, bet, lsn, rsn, op);
/* ... */
}
/* ... */
static void
cir_sky (
/* ... */
double *rho, /* dist from earth: in as geo, back as geo or topo */
/* ... */)
{
/* ... */
printf("/nDEBUG1: *rho=%f/n", *rho);
El archivo C completo está aquí:
Hubiera esperado que el valor mostrado en el primer printf()
fuera el mismo que el mostrado por el segundo, ya que pasar un puntero a un doble no debería dar como resultado un valor diferente. Y bajo GCC son, de hecho, siempre el mismo valor. En la compilación Visual Studio de 32 bits, siempre son los mismos. Pero cuando este código se compila con Visual Studio en una arquitectura de 64 bits, ¡los dos valores dobles son diferentes!
https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.18/job/4xu7abnl9vx3n770#L573
rho from sqrt(): 0.029624
DEBUG1: *rho=0.000171
Esto es desconcertante Me preguntaba: ¿el código entre donde se calcula el rho
y dónde finalmente se pasa el puntero destruye de algún modo el valor mediante la mala aritmética del puntero? Así que agregué un último printf()
, justo encima de la llamada cir_sky()
, para ver si el valor ya ha sido alterado en ese punto o si se altera en el transcurso de la llamada:
printf("/nrho about to be sent: %f/n", rho);
cir_sky (np, lpd, psi, rp, &rho, lam, bet, lsn, rsn, op);
Aquí está esa línea en el contexto de todo el archivo:
¿Y adivina qué?
Al agregar printf()
corrigió el error: ¡el puntero pasado a rho
ahora puede desreferenciarse al valor correcto!
Como se puede ver aquí:
https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.19/job/s3nh90sk88cpn2ee#L567
rho from sqrt(): 0.029624
rho about to be sent: 0.029624
DEBUG1: *rho=0.029624
Estoy desconcertado.
¿Qué caso límite del estándar C me estoy encontrando aquí? ¿Por qué el mero uso del valor rho
en el ámbito de nivel superior de esta función obliga al compilador de Microsoft a conservar correctamente su valor? ¿El problema es que rho
está configurado y usado dentro de un bloque, y Visual Studio no se digna a preservar su valor fuera de ese bloque debido a una peculiaridad del estándar C que nunca he internalizado del todo?
Puede ver la salida completa de compilación en el enlace AppVeyor arriba. El paso de compilación particular para este archivo C, en caso de que el problema sea cómo se invoca Visual Studio o las opciones de compilación, es:
C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/Bin/amd64/cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -Ilibastro-3.7.7 -IC:/Python27-x64/include -IC:/Python27-x64/PC /Tclibastro-3.7.7/circum.c /Fobuild/temp.win-amd64-2.7/Release/libastro-3.7.7/circum.obj
circum.c
libastro-3.7.7/circum.c(126) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(127) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(139) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(140) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(295) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(296) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(729) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
libastro-3.7.7/circum.c(730) : warning C4244: ''='' : conversion from ''double'' to ''float'', possible loss of data
Ninguna de esas advertencias es, por lo que puedo ver, para el código involucrado en este rompecabezas en particular, e incluso si lo fueran, todo lo que significarían es que un valor flotante podría ser menos preciso (de aproximadamente 15 dígitos de precisión decimal a 7) , no es que pueda cambiar completamente.
Aquí, de nuevo, están los resultados de las dos ejecuciones de compilación y prueba, la primera de las cuales falló y la segunda, debido a printf()
? - tenido éxito:
https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.18/job/4xu7abnl9vx3n770
https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.19/job/s3nh90sk88cpn2ee
Ambos son exactamente para la misma arquitectura, según AppVeyor:
Environment: PYTHON=C:/Python27-x64, PYTHON_VERSION=2.7.x, PYTHON_ARCH=64, WINDOWS_SDK_VERSION=v7.0
Mi rápido vistazo a este código no produjo nada que se destaque como incorrecto o problemático. Pero, cuando un printf
resuelve el problema, eso significa que hay algo de no determinismo presente. Analicemos las posibles causas:
- Concurrencia: carreras de datos: más comunes, pero usted dice que tiene un solo hilo.
- Memoria no inicializada:
rho
se inicializa aquí, pero, tal vez algo en otro lugar no es y está estropeando las cosas. Me gustaría ejecutar valgrind (en Linux) y AdressSanitizer y otros desinfectantes (también deberían estar disponibles en clang y gcc para Windows) para ver si se les ocurría algo. - Punteros salvajes y otros accesos fuera de límites: nada en el código que vemos aquí, pero está llamando a otras funciones. De nuevo, ejecute valgrind y sanitizers.
- Si los pasos anteriores son insuficientes, el siguiente candidato más probable es un error de MSVC. MSVC es conocido por estropear las cosas en un código complejo, y esto es algo complejo. Muchas veces reordené el código solo para hacer feliz a MSVC. A veces, desactivar la optimización ayuda, a veces no es así. Lo mismo para probar diferentes opciones de compilación. Algunas veces hay una actualización / parche que ayuda, otras veces no. Lo mismo para la próxima versión de MSVC. Sugeriría mirar desensamblador en el depurador, pero dices que no tienes acceso a una máquina con Windows. La mejor opción sería intentar simplificar el código: reducir las funciones, reducir el número de argumentos.
- Hay otras causas posibles. Por ejemplo, tal vez la pila se arruinó por alguna razón, tal vez al interactuar con el tiempo de ejecución de Python. Intenta compilarlo y ejecutarlo como código C "normal", en lugar de una extensión de Python. Elimina las llamadas a otras funciones (si se equivoca con los cálculos, no importa, solo estás tratando de descubrir el problema).
En cualquier caso, le recomiendo que ponga sus manos en una máquina con Windows y la depure. Esa es la mejor manera de llegar al fondo de esos problemas, en mi experiencia.