2010-04-03 20 views
6

¿Hay alguna manera de redondear números en C?Cómo redondear los números de coma flotante al entero más cercano en C?

No quiero usar ceil y piso. ¿Hay otra alternativa?

me encontré con este fragmento de código cuando busqué en Google para la respuesta:

(int)(num < 0 ? (num - 0.5) : (num + 0.5)) 

La línea anterior siempre imprime el valor como 4, incluso cuando el flotador num = 4,9.

+0

Hay muchos tipos diferentes de redondeo: ¿cuál (es) desea usar? Por favor, publique ejemplos del comportamiento deseado. –

+3

Creo que el problema está en otra parte, que definitivamente debería imprimir 5 para una entrada de 4.9. – IVlad

+0

Sí, la conversión de un tipo de punto flotante a un tipo de entero que puede representar un número del signo y la magnitud requeridos debería funcionar simplemente truncando los decimales; este código hace que el ± 0.5 cause que este truncamiento redondee el valor original lejos de cero. – Arkku

Respuesta

10

4.9 + 0.5 es 5.4, que posiblemente no puede redondear a 4 a menos que su compilador se rompa seriamente.

Acabo de confirmar que el código de Google proporciona la respuesta correcta para 4.9.

[email protected]:~$ cat round.c 
#include <stdio.h> 

int main() { 
    float num = 4.9; 
    int n = (int)(num < 0 ? (num - 0.5) : (num + 0.5)); 
    printf("%d\n", n); 
} 
[email protected]:~$ make round && ./round 
cc  round.c -o round 
5 
[email protected]:~$ 
+0

Hmmm .... Tienes razón ... La declaración se puede simplificar aún más a: int n = (num <0)? (num - 0.5): (num + 0.5); He comprobado esto y funciona sin problemas. ¿Puede explicar cómo funciona la comparación (num <0)? Inserté un punto de interrupción en mi IDE y vi que la comprobación de condición (num <0) siempre apunta a FALSE, que debería ejecutarse (num + 0.5) siempre – webgenius

+0

No estoy seguro de lo que está preguntando, ¿pero una expresión 'a? b: c' evalúa a 'b' si' a' es verdadero y a 'c' en caso contrario. En este caso, la idea es mover el valor de 'num' lejos de cero por' 0.5' antes de convertirlo a int truncando los decimales. De esta forma, el truncamiento redondeará al int más cercano (por ejemplo, 0.5 + 0.5 = 1.0 y 0.99 + 0.5 = 1.49 ambos truncarán a 1). – Arkku

+0

¿Pero por qué se hace la comparación (num <0)? num siempre será 4.9 – webgenius

3

Una solución general es utilizar rint() y establecer el modo de redondeo FLT_ROUNDS según sea apropiado.

-4

trate de mover los soportes en su solución anterior, para que se lea:

(int)(num < 0) ? (num - 0.5) : (num + 0.5) 

Usando num como 4.9 se redondea a 5 en mi máquina.

+1

Esto no tiene otro efecto que el de posiblemente generar una advertencia cuando asigna esta expresión a un int. Además, el molde '(int)' es redundante, ya que 'num <0' ya tiene el tipo' int'. –

+0

¿Qué quiere decir que esto no puede funcionar? Esto es lo mismo que la respuesta que tiene 4 votos. –

+2

No dije que no podía funcionar, dije que no tiene ningún efecto, por lo que quise decir que eliminar los paréntesis como lo hizo no cambia nada. –

1

creo que lo que estás buscando es: int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);

+1

"No quiero usar el cielo raso y el piso" ... – IVlad

+3

Si el interrogador realmente no quiere usar el cielo raso y el piso, pero realmente está contento de usar la conversión incorporada a int, entonces la pregunta puede ser presentada en "preguntas extravagantes estilo de entrevista que implican una restricción antinatural para ilustrar algún punto completamente entendido solo por el entrevistador". Desafortunadamente eso no cabe en una etiqueta. –

+0

Steve, tienes razón. Esta es una pregunta frecuente en las entrevistas. – webgenius

0

Usted puede ser capaz de utilizar fesetround() en fenv.h (introducido en C99). Los posibles argumentos son las macros FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO y FE_UPWARD, pero tenga en cuenta que no todas están necesariamente definidas, solo que son compatibles con la plataforma/implementación. Luego puede usar las diversas funciones round, rint y nearbyint en math.h (también C99). De esta forma puede establecer el comportamiento de redondeo deseado una vez y llamar a la misma función independientemente de si el valor es positivo o negativo.

(con, por ejemplo lround por lo general, ni siquiera es necesario establecer la dirección de redondeo para el uso normal de conseguir por lo general lo que quiere.)

3

no estoy seguro de que sea una buena idea. Ese código depende de los moldes, y estoy bastante seguro de que el truncamiento exacto no está definido.

float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num); 

Yo diría que esta es una manera mucho mejor (que es básicamente lo que ha escrito Shiroko) ya que no depende de ningún moldes.

1

el código de Google funciona correctamente. La idea detrás de esto es redondear cuando el decimal es menor que .5 y redondear de otro modo. (int) coloca el flotante en un tipo int que simplemente suelta el decimal. Si agrega .5 a un num positivo, obtendrá drop al siguiente int. Si resta 0,5 de un negativo, hace lo mismo.

0
int round(double x) 
{ 
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1); 
} 

Será más rápido que una versión con cielo raso y piso.

+0

No es una solución C. 'int (x + 0.5)' no es válido C. El algoritmo falla para 'x menos de 0.5,' x' no cerca del rango 'int'. – chux

0

Valor redondo x a precisión p, donde 0 < p < infinito. (f.ex. 0.25, 0.5, 1, 2, ...)

float RoundTo(float x, float p) 
{ 
    float y = 1/p; 
    return int((x+(1/(y+y)))*y)/y; 
} 

float RoundUp(float x, float p) 
{ 
    float y = 1/p; 
    return int((x+(1/y))*y)/y; 
} 

float RoundDown(float x, float p) 
{ 
    float y = 1/p; 
    return int(x*y)/y; 
} 
+0

Lo que ha publicado no se compila en C: el idioma en el que está etiquetada esta publicación. Tal vez estás codificando por otro idioma? – chux

+0

Además, en C++, el uso de 'int (...)' falla si 'x * (...)' supera el rango de 'int'. – chux

+0

Es Arduino C/C++, lo siento si eso no es "real" C. Allí ese int (float x) devuelve solo un entero (quitando decimales). ¿Cuál es la forma "correcta" de eliminar solo decimales? – JJussi

1

Para redondear un float en C, hay 3 <math.h> funciones para satisfacer la necesidad. Recomendar rintf().

float nearbyintf(float x); 

Los nearbyint funciones alrededor de su argumento a un valor entero en formato de punto flotante, utilizando la dirección actual de redondeo y sin elevar la ‘’ excepción de punto flotante ‘inexacta’. C11dr §7.12.9.3 2

o

float rintf(float x); 

Los rint funciones difieren de los nearbyint funciones (7.12.9.3) solo en que el rint funciones pueden elevar el '' inexacta' 'excepción de punto flotante si el resultado difiere en valor del argumento. C11dr §7.12.9.4 2

o

float roundf(float x); 

Las funciones round redondas su argumento al valor entero más cercano en formato de punto flotante de redondeo, los casos de reinserción lejos de cero, sin tener en cuenta la dirección de redondeo actual. C11dr §7.12.9.6 2


Ejemplo

#include <fenv.h> 
#include <math.h> 
#include <stdio.h> 

void rtest(const char *fname, double (*f)(double x), double x) { 
    printf("Clear inexact flag  :%s\n", feclearexcept(FE_INEXACT) ? "Fail" : "Success"); 
    printf("Set round to nearest mode:%s\n", fesetround(FE_TONEAREST) ? "Fail" : "Success"); 

    double y = (*f)(x); 
    printf("%s(%f) --> %f\n", fname,x,y); 

    printf("Inexact flag    :%s\n", fetestexcept(FE_INEXACT) ? "Inexact" : "Exact"); 
    puts(""); 
} 

int main(void) { 
    double x = 8.5; 
    rtest("nearbyint", nearbyint, x); 
    rtest("rint", rint, x); 
    rtest("round", round, x); 
    return 0; 
} 

salida

Clear inexact flag  :Success 
Set round to nearest mode:Success 
nearbyint(8.500000) --> 8.000000 
Inexact flag    :Exact 

Clear inexact flag  :Success 
Set round to nearest mode:Success 
rint(8.500000) --> 8.000000 
Inexact flag    :Inexact 

Clear inexact flag  :Success 
Set round to nearest mode:Success 
round(8.500000) --> 9.000000 
Inexact flag    :Exact 

Qué es débil sobre el código de OP?

(int)(num < 0 ? (num - 0.5) : (num + 0.5)) 
  1. En caso num tienen un valor no cerca de la gama int, los resultados elenco (int) un comportamiento indefinido.

  2. Cuando num +/- 0.5 da como resultado una respuesta inexacta. Esto es improbable aquí ya que 0.5 es un double y hace que la adición ocurra con una precisión mayor que float. Cuando num y 0.5 tienen la misma precisión, agregar 0.5 a un número puede dar como resultado respuesta numérica redondeada. (Este no es el redondeo de números enteros de la publicación de OP.) Ejemplo: el número justo por debajo de 0,5 debe redondearse a 0 por objetivo de OP, pero num + 0.5 da como resultado una respuesta exacta entre 1.0 y la más pequeña double apenas inferior a 1.0. Como la respuesta exacta no es representable, esa suma redondea, por lo general, a 1.0, lo que lleva a una respuesta incorrecta. Una situación similar ocurre con grandes números.dilema


de OP sobre "La línea anterior siempre imprime el valor como 4 incluso cuando float num =4.9." no es explicable como se dijo. Se necesita código/información adicional. Sospecho que OP pudo haber usado int num = 4.9;.


// avoid all library calls 
// Relies on UINTMAX_MAX >= FLT_MAX_CONTINUOUS_INTEGER - 1 
float my_roundf(float x) { 
    // Test for large values of x 
    // All of the x values are whole numbers and need no rounding 
    #define FLT_MAX_CONTINUOUS_INTEGER (FLT_RADIX/FLT_EPSILON) 
    if (x >= FLT_MAX_CONTINUOUS_INTEGER) return x; 

    // Positive numbers 
    // Important: _no_ precision lost in the subtraction 
    // This is the key improvement over OP's method 
    if (x > 0) { 
    float floor_x = (float)(uintmax_t) x; 
    if (x - floor_x >= 0.5) floor_x += 1.0f; 
    return floor_x; 
    } 

    if (x < 0) return -my_roundf(-x); 
    return x; // x is 0.0, -0.0 or NaN 
} 

Probado poco - lo hará más adelante cuando tenga tiempo.

+0

También sería bueno tener ejemplos de establecer la dirección de redondeo actual y la bandera inexacta. –

+1

@Ciro Ejemplo agregado. – chux

Cuestiones relacionadas