2009-06-15 16 views
9

estoy usando el siguiente código para el redondeo a 2DP:Redondeo duplica - 0,5 - sprintf

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double 

Se redondea con éxito 6.666 a 6.67 pero no funciona correctamente cuando el redondeo 5.555. Devuelve 5.55, mientras que debería (al menos en mi opinión) devolver 5.56.

¿Cómo puedo hacer para redondear cuando el siguiente dígito es 5? es decir, devolver 5.56.

editar: Ahora me doy cuenta de que esto está sucediendo porque cuando ingreso 5.555 con cin se obtiene guardado como 5.554999997.

Voy a intentar redondear en dos etapas: primero a 3dp y luego a 2dp. cualquier otra idea (más elegante)?

Respuesta

11

Parece que tiene que usar la función de redondeo matemático para redondear correctamente.

printf("%.2f %.2f\n", 5.555, round(5.555 * 100.)/100.); 

Esto da el siguiente resultado en mi máquina:

5.55 5.56 
+0

Esto parece un poco más fiable que la idea de añadir 0,0005, pero ambos funcionaría supongo. ¡Gracias por tu ayuda! – Meir

+0

Parece ser un error, de verdad. ¡5.5551 se redondea correctamente! – ypnos

+6

No es un error, @ypnos, aún no alcanzas los límites del punto flotante. 5.5551 se representa como 5.555100000000000370 ... que redondeará, 5.555 se representa como 5.5549999999 ... que redondeará hacia abajo. Puede ver que es un problema de representación al probar su código con 5.5500000000000000001; ambas salidas son 5.55 incorrectas en ese caso porque el número NO ES 5.55000 ... 1 cuando está almacenado. Es 5.549999 ... – paxdiablo

11

El número 5.555 no puede ser representado como un número exacto de IEEE754. La impresión de las constantes 5.555 con "%.50f" resultados en:

5.55499999999999971578290569595992565155029300000000 

por lo que se se redondea hacia abajo. Trate de usar esto en su lugar:

printf ("%.2f\n",x+0.0005); 

aunque hay que tener cuidado de números que puede puede representar exactamente, ya que va a redondearse erróneamente por esta expresión.

Necesita comprender las limitaciones de las representaciones de punto flotante. Si es importante que obtenga precisión, puede usar (o codificar) un BCD u otra clase decimal que no tenga la deficiencia de la representación IEEE754.

+0

Esto no afecta su respuesta, pero tenía curiosidad: ¿qué idioma usó para imprimir 5.55499999999999971578290569595992565155029300000000? En realidad debería ser 5.55499999999999971578290569595992565155029296875000 –

+0

@Rick, muy probablemente gcc bajo CygWin pero es difícil recordarlo hace un año :-) Obtuve el mismo resultado que en Ubuntu10. Pero creo que los dobles solo garantizan 15 dígitos decimales de precisión de todos modos, así que puede estar usando un formato interno más amplio (tengo una vaga idea de que los cálculos en al menos un sistema usaron 80bits internamente, pero eso habría sido hace mucho tiempo: obtener 48 dígitos decimales como con su número, necesitaría aproximadamente 160 bits de precisión). – paxdiablo

+0

OK, gracias. Sabía que no podía ser Visual C++ (no puede imprimir tantos dígitos con precisión) y parecía tener muy pocos dígitos para gcc/glibc en Linux (lo que le permite imprimirlos todos). En cualquier caso, puede obtener esos muchos dígitos con un doble estándar (53 bits): es la representación decimal exacta de lo que contiene el doble (incluso si solo puede ser una aproximación de 15 dígitos de lo que se le asignó). –

-2

También puede hacer esto (ahorra multiplicar/dividir):

printf("%.2f\n", coef[i] + 0.00049999); 
+0

Esto redondeará 5.549999 a 5.56 ;-) – ypnos

+0

@ypnos, realmente necesita verificar las cosas antes de publicar; NO le da 5.56 en absoluto. – paxdiablo

+1

Lo siento error. Aún deberías ver mi punto. 5.544999 + 0.0005 se redondea a 5.55. Debe redondearse claramente a 5.54. No hace falta ciencia espacial para darse cuenta de que agregar un sesgo no solucionará misteriosamente un problema de redondeo sin introducir otros nuevos. – ypnos

1

Esta pregunta se marca C++, así que voy a proceder de acuerdo con esa suposición. Tenga en cuenta que las secuencias C++ se redondearán, a diferencia de la familia C printf. Todo lo que tiene que hacer es proporcionar la precisión que desea y la biblioteca de secuencias se redondeará para usted. Solo lo estoy lanzando en caso de que todavía no tenga una razón para no usar transmisiones.

2

¿Qué tal esto para otra posible solución:

printf("%.2f", _nextafter(n, n*2)); 

La idea es aumentar el número de distancia de cero (el n * 2 recibe la señal derecha) por la menor cantidad posible representable mediante aritmética de punto flotante.

Ej:

double n=5.555; 
printf("%.2f\n", n); 
printf("%.2f\n", _nextafter(n, n*2)); 
printf("%.20f\n", n); 
printf("%.20f\n", _nextafter(n, n*2)); 

Con rendimientos de MSVC:

5.55 
5.56 
5.55499999999999970000 
5.55500000000000060000