Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

146
Vistas
Respuestas diferentes eliminando una instrucción printf

Este es el enlace a la pregunta sobre el juez en línea UVa.
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=29&page=show_problem&problem=1078

mi codigo c es

 #include <stdio.h> double avg(double * arr,int students) { int i; double average=0; for(i=0;i<students;i++){ average=average+(*(arr+i)); } average=average/students; int temp=average*100; average=temp/100.0; return average; } double mon(double * arr,int students,double average) { int i; double count=0; for(i=0;i<students;i++){ if(*(arr+i)<average){ double temp=average-*(arr+i); int a=temp*100; temp=a/100.0; count=count+temp; } } return count; } int main(void) { // your code goes here int students; scanf("%d",&students); while(students!=0){ double arr[students]; int i; for(i=0;i<students;i++){ scanf("%lf",&arr[i]); } double average=avg(arr,students); //printf("%lf\n",average); double money=mon(arr,students,average); printf("$%.2lf\n",money); scanf("%d",&students); } return 0; }

Una de las entradas y salidas son
Aporte
3
0.01
0.03
0.03
0

Producción
$0.01

mi salida es
$0.00.

Sin embargo, si elimino el comentario de la línea printf("%lf",average);

La salida es la siguiente
0.02 //Este es el promedio
$0.01

Estoy ejecutando el código en ideone.com

Explique por qué sucede esto.

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Creo que he encontrado al culpable y una explicación razonable.

En los procesadores x86, la FPU funciona internamente con precisión extendida , que es un formato de 80 bits. Todas las instrucciones de punto flotante operan con esta precisión. Si realmente se requiere un double , el compilador generará código para convertir el valor de precisión extendida en un valor de doble precisión. Crucialmente, el printf comentado obliga a tal conversión porque los registros FPU deben guardarse y restaurarse a través de esa llamada de función, y se guardarán como double s (tenga en cuenta que avg y mon están en línea, por lo que no se guarda ni restaura allí) .

De hecho, en lugar de printf podemos usar la línea static double dummy = average; para forzar la double conversión, lo que también hace que el error desaparezca: http://ideone.com/a1wadn

Su valor de average es cercano pero no exactamente 0.02 debido a imprecisiones de coma flotante. Cuando hago todos los cálculos explícitamente con long double e imprimo el valor de average , es lo siguiente:

 long double: 0.01999999999999999999959342418532 double: 0.02000000000000000041633363423443

Ahora puedes ver el problema. Cuando agrega el printf , el average se fuerza a un double que lo empuja por encima de 0.02. Pero, sin el printf , el average será un poco menos de 0.02 en su formato nativo de precisión extendida.

Cuando haces int a=temp*100; aparece el error. Sin la conversión, esto hace a = 1 . Con la conversión, esto hace a = 2 .

Para arreglar esto, simplemente use int a=round(temp*100); - todos sus errores extraños deberían desaparecer.


Cabe destacar que este error es extremadamente sensible a los cambios en el código. Cualquier cosa que haga que los registros se guarden (como un printf prácticamente en cualquier lugar) hará que el error desaparezca. Por lo tanto, este es un ejemplo extremadamente bueno de heisenbug : un error que desaparece cuando intentas investigarlo.

over 4 years ago · Santiago Trujillo Denunciar

0

@nneonneo respondió bien a la mayor parte del problema: las compilaciones ligeramente variantes dan como resultado un código de punto flotante ligeramente diferente que da como resultado casi la misma respuesta double , excepto que una respuesta está justo debajo de 2.0 y la otra en o casi 2.0.

Me gustaría agregar sobre la importancia de no usar la conversión a int para el redondeo de coma flotante.

Código como double temp; ... int a=temp*100; acentúe esta diferencia dando como resultado a con un valor de 1 o 2 ya que la conversión a int es efectiva "truncar hacia cero" - suelte la fracción.

En lugar de redondear a cerca de 0,01 con un código como:

 double temp; ... int a = temp*100; // Problem is here temp = a/100.0;

No use int en absoluto. Usar

 double temp; ... temp = round(temp*100.0)/100.0;

Esto no solo proporciona respuestas más consistentes (ya que es poco probable que temp tenga valores cercanos a medio centavo ), sino que también permite valores de temp fuera del rango int . temp = 1e13; int a = temp/100; ciertamente da como resultado un comportamiento indefinido.

No use conversión a int para redondear números de punto flotante: use round()

roundf() , roundl() , floor() , ceil() , etc. también pueden ser útiles. @jeff

over 4 years ago · Santiago Trujillo Denunciar

0

Estás dividiendo un doble por un número entero, que es una división de números enteros. En este caso dará un valor de 0.

Si lanzas a estudiante a un doble, debería darte el resultado adecuado.

 average=average/(double)students;

Puede haber otras ubicaciones que sean necesarias dependiendo de su aritmética.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda