2010-01-01 10 views
5

Tengo un código que se comporta de manera diferente bajo Mac OSX y Linux (Ubuntu, Fedora, ...). Esto se refiere a la conversión de tipos en operaciones aritméticas dentro de instrucciones printf. El código está compilado con gcc/g ++.Mecanografía con declaraciones printf bajo Mac OSX y Linux

La siguiente

#include <stdio.h> 
int main() { 
    float days = (float) (153*86400)/86400.0; 
    printf ("%f\n", days); 
    float foo = days/30.6; 
    printf ("%d\n", (int) foo); 
    printf ("%d\n", (int) (days/30.6)); 
    return 0; 
} 

genera en Linux

153.000000 
5 
4 

y en Mac OS X

153.000000 
5 
5 

¿Por qué?

Para mi sorpresa esto aquí funciona tanto en Mac OS X y Linux

printf ("%d\n", (int) (((float)(153 * 86400)/86400.0)/30.6)); 
printf ("%d\n", (int) (153/30.6)); 
printf ("%.16f\n", (153/30.6)); 

¿Por qué? No tengo ni idea. GRACIAS.

+0

realmente no es una respuesta, pero ¿por qué hacer esto:' días flotantes = (flotante) (153 * 86400)/86400.0; '? Eso debería ser igual a '153.0' ya que la multiplicación y la división se cancelan mutuamente. –

+0

Inicialmente el código era algo así como "float days = (float) delta/86400.0;" Quería simular el error que aparece con 153 días (o los segundos correspondientes). Como el código funciona durante 152 días. Podría haber ingresado 13219200 en vez de 153 * 86400, por supuesto. –

+0

No lo veo ahora mismo. ¿Estás usando las mismas o diferentes versiones de gcc en los dos entornos? También los mismos o diferentes procesadores? ¿64 bit o 32 bit? – dmckee

Respuesta

5

probar esto:

#include <stdio.h> 
int main() { 
    float days = (float) (153*86400)/86400.0; 
    printf ("%f\n", days); 
    float foo = days/30.6; 
    printf ("%d\n", (int) foo); 
    printf ("%d\n", (int) (days/30.6)); 
    printf ("%d\n", (int) (float)(days/30.6)); 
    return 0; 
} 

Aviso lo que sucede? La conversión doble a flotante es la culpable. Recuerde que el flotador siempre se convierte en el doble en una función varargs. Aunque no estoy seguro de por qué los macos serían diferentes. Mejor (o peor) implementación de aritmética IEEE?

+0

Por cierto, el resultado es 5 5. –

+0

Éste funciona: devuelve 5 4 5 en Linux como se esperaba. –

+0

Macos es diferente debido a los registros x86 de 80 bits que utiliza gcc. Con la opción '-ffloat-store' de gcc, obtengo' 153.000000 5 5' tanto en Linux como en Mac. Por supuesto, el problema principal es esperar números exactos en los cálculos de coma flotante. –

1

¿Ambos se ejecutan en el mismo procesador? Mi suposición es que tiene que ver con endianess de la plataforma, no con el sistema operativo. Pruebe también (int) ((float) (days/30.6)) en lugar de (int) (days/30.6).

Otro aspecto a tener en cuenta es si las versiones del compilador son las mismas.

dudo que tiene que ver con printf, intente esto:

#include <iostream> 
int main() { 
    float days = (float) (153*86400)/86400.0; 
    std::cout << days << std::endl; 
    float foo = days/30.6; 
    std::cout << (int) foo << std::endl; 
    std::cout << (int) (days/30.6) << std::endl; 
    return 0; 
} 

Y publicar el resultado en los comentarios, por favor.

1

Hmmmmmmm ... Sospecho que hay alguna diferencia debido a que la máquina Linux es una de 32 bits (¿estoy en lo cierto?) Y la Mac es una máquina de 64 bits. En mi máquina Linux de 64 bits obtengo el segundo conjunto de resultados.

+0

Sería interesante comparar la salida del ensamblador para Intel de 32 bits y Intel de 64 bits. –

+0

Me lo pregunté también: en MacOS X 10.6.2, con GCC 4.2.1 (pero una biblioteca de C basada en BDS, no glibc), obtuve los mismos resultados para las compilaciones de 32 bits y de 64 bits. –

+0

@Jonathans: pau significaba diferentes arquitecturas (es decir, CPU). –

1

Estoy comenzando a preguntarme acerca de los problemas de representación en coma flotante. No creo que 30.6 tenga una representación exacta de IEEE. Quizás tengas "suerte" con los problemas de redondeo.

Otra posibilidad es diferente manejo compilador de esta línea:

float days = (float) (153*86400)/86400.0; 

donde como un ser humano puedo ver que los segundos por poco días anula, pero el compilador podría perder la oportunidad de hacer el plegado constante si trata uno como en un contexto entero y el otro como en un contexto de punto flotante posterior. ¿Algún gurú de las normas quiere incidir en la presencia o ausencia de puntos de secuencia allí?

+0

30.6 no tiene una representación binaria exacta en coma flotante, ya que no tiene "5" como último dígito. Todos los cálculos que lo usan serán muy poco inexactos. –

1

Un cálculo es como float y uno como double, y deben estar redondeando de forma diferente en Linux. Por qué este no es el comportamiento de MacOSX, no lo sé, sobre todo porque no te molestas en especificar nada sobre la computadora MacOSX. ¿Es una Mac real? PPC o Intel?

Nunca, nunca, confíe en los cálculos de coma flotante para salir exactamente de la manera que desee. Siempre redondo a int, nunca truncar.

+0

Real Mac. Basado en Intel. La última MacBook Pro. –

+0

OS X: 10.6.2, 64 bits. gcc 4.2.1 Linux: Ubuntu 9.10, 32 bits, gcc 4.4.1 –

4

Espero que la respuesta esté relacionada de alguna manera con la asignación de 32 bits a la variable float que se convierte en un doble antes de imprimir, dejando menos bits significativos de los que esperaría si pasara un doble completo, como en el segundo expresión.Se pone complicado, lidiando con la aritmética de punto flotante. Todavía me gusta la cita de Kernighan y Pike libro clásico 'Los Elementos de Programación Style', donde dicen:

Un programador sabio dijo una vez, "Números de punto flotante son como pequeños montones de arena, cada vez que se mueve uno, pierdes un poco de arena y recoges un poco de tierra ".

Esta es una ilustración del punto. También muestra por qué la aritmética decimal de coma flotante como en el estándar revisado IEEE 754 es una buena idea.

3

Su valor por days es un cálculo de coma flotante, que con precisión infinita producirá exactamente 5.0.

Si, con su precisión limitada, se obtiene un resultado que es la más mínima poco más pequeño a 5,0 el yeso a un int producirá un entero menor que 5.

El problema aquí es el uso incorrecto de aritmética de punto flotante. Probablemente deberías estar redondeando.

La razón que se está viendo la discrepancia es o bien una diferencia en el manejo de la CPU de punto flotante o (más probablemente) optimizadores de sus compiladores varían en el ajuste de los cálculos que se realizan en tiempo de ejecución y en qué orden.

3

Para principiantes, lea What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Porque 30.6 no es exactamente representable en IEEE 754 coma flotante, los resultados precisos que obtenga no serán exactamente correctos. Entonces puede obtener un poco menos de 5.0 antes de convertir a un número entero, y luego el molde entero redondea a 4.

El resultado exacto que obtiene depende de muchos factores, como la forma en que se calculan los resultados intermedios. Su compilador podría estar generando código para usar la pila de punto flotante x87, que usa registros de 80 bits para cálculos intermedios. Alternativamente, puede estar utilizando SSE2 (el predeterminado en Mac OS X), que utiliza registros vectoriales de 128 bits divididos en 4x32 bits o 2x64 bits. Compruebe la salida de conjunto de su compilador para el tipo de operaciones de coma flotante utilizadas. Consulte this page para obtener una lista de las opciones de línea de comandos de GCC que puede usar para controlar el tipo de instrucciones de coma flotante utilizadas, particularmente la opción -mfpmath.

+0

THX para los enlaces. –

1

Aquí hay un código que debe ilustrar de wat que suceden un poco mejor que el código original:

#include <stdio.h> 

int main(void) 
{ 
    volatile double d_30_6 = 30.6; 
    volatile float f_30_6 = 30.6; 

    printf("%.16f\n", 153/30.6); // compile-time 
    printf("%.16f\n", 153/d_30_6); // double precision 
    printf("%.16f\n", 153/f_30_6); // single precision 

    return 0; 
} 

Los volatile variables de obligar al compilador a no optimizar el cálculo de distancia, independientemente del nivel de optimización ...

0

números de punto flotante prueban la existencia de Dios por la negación, ya que son sin duda la obra del diablo ...


Un día después de un largo día de funcionamiento del universo Dios y Satanás se reunieron por una cerveza y comenzó a recordar. "Oye, ¿recuerdas las cosas que le pusimos a ese tipo, Job?" dijo Dios

"Sí", respondió Satanás, "esos eran los días, ¿verdad? Un montón de golpear violentamente y contundente para perdición eterna ..."

"no hablar de toda la profecía y las plagas y todo", dijo Dios . "Sí, esos fueron esos días". Dios suspiró. "Diga, sé que ha pasado un tiempo pero, um, ¿cómo te sientes para hacer algo así otra vez?".

"Es curioso que lo menciones", dijo Satanás sin problemas. "Sabes que siempre me ha interesado la tecnología ...".

"Claro", dijo Dios, "muchas oportunidades para que la gente venda sus almas para otra ronda de financiación, ¿eh?" Él se rió entre dientes. "Entonces, ¿qué tienes en mente?"

"Bueno", dijo Satanás: "He puesto a mis mejores demonios en eso. Han estado trabajando como el mismísimo diablo para idear algo nuevo y creo que lo tienen. Es un poco algo llamamos 'punto flotante' ... "