¿Cómo puedo redondear un valor flotante (como 37.777779) a dos decimales (37.78) en C?
¿Cómo puedo redondear un valor flotante (como 37.777779) a dos decimales (37.78) en C?
Respuestas:
Si solo desea redondear el número para fines de salida, la "%.2f"
cadena de formato es la respuesta correcta. Sin embargo, si realmente desea redondear el valor de coma flotante para un mayor cálculo, algo como lo siguiente funciona:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
Observe que hay tres reglas de redondeo diferentes que puede elegir: redondear hacia abajo (es decir, truncar después de dos decimales), redondear al más cercano y redondear hacia arriba. Por lo general, desea redondear al más cercano.
Como varios otros han señalado, debido a las peculiaridades de la representación de coma flotante, estos valores redondeados pueden no ser exactamente los valores decimales "obvios", pero estarán muy, muy cerca.
Para obtener más (¡mucho!) Más información sobre el redondeo, y especialmente sobre las reglas de desempate para el redondeo al más cercano, consulte el artículo de Wikipedia sobre el redondeo .
doubles
también de alguna manera? No parece hacer el trabajo que quiero :( (usando floor
y ceil
).
Usando % .2f en printf. Solo imprime 2 puntos decimales.
Ejemplo:
printf("%.2f", 37.777779);
Salida:
37.77
float
rango, ya que val * 100
podría desbordarse.
Suponiendo que está hablando de redondear el valor de impresión, la respuesta de Andrew Coleson y AraK es correcta:
printf("%.2f", 37.777779);
Pero tenga en cuenta que si desea redondear el número exactamente a 37.78 para uso interno (por ejemplo, para compararlo con otro valor), entonces esta no es una buena idea, debido a la forma en que funcionan los números de coma flotante: generalmente no desea hacer comparaciones de igualdad para coma flotante, en su lugar use un valor objetivo +/- un valor sigma. O codifique el número como una cadena con una precisión conocida y compárelo.
Vea el enlace en la respuesta de Greg Hewgill a una pregunta relacionada , que también cubre por qué no debe usar coma flotante para los cálculos financieros.
printf("%.*f", (int)precision, (double)number);
Qué tal esto:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Si quieres escribir en C-string:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
No hay forma de redondear un float
a otro float
porque el redondeado float
puede no ser representable (una limitación de los números de coma flotante). Por ejemplo, supongamos que redondea 37.777779 a 37.78, pero el número representable más cercano es 37.781.
Sin embargo, puede "redondear" a float
mediante una función de cadena de formato.
float
a n decimales y luego esperar que el resultado siempre tenga n decimales. Todavía obtendrá un float
, simplemente no el que esperaba.
Además, si está utilizando C ++, puede crear una función como esta:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Luego puede generar cualquier doble myDouble
con n
lugares después del punto decimal con un código como este:
std::cout << prd(myDouble,n);
Aún puedes usar:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
ejemplo:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
En C ++ (o en C con conversiones de estilo C), puede crear la función:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
Entonces std::cout << showDecimals(37.777779,2);
produciría: 37.78.
Obviamente, realmente no necesita crear las 5 variables en esa función, pero las dejo allí para que pueda ver la lógica. Probablemente haya soluciones más simples, pero esto funciona bien para mí, especialmente porque me permite ajustar la cantidad de dígitos después del decimal, según sea necesario.
Utilice siempre la printf
familia de funciones para esto. Incluso si desea obtener el valor como flotante, es mejor usarlo snprintf
para obtener el valor redondeado como una cadena y luego analizarlo de nuevo con atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
Digo esto porque el enfoque que se muestra en la respuesta actualmente más votada y en varias otras aquí, multiplicando por 100, redondeando al entero más cercano y luego dividiendo por 100 nuevamente, tiene fallas de dos maneras:
Para ilustrar el primer tipo de error, la dirección de redondeo a veces es incorrecta, intente ejecutar este programa:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Verás esta salida:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Tenga en cuenta que el valor con el que comenzamos fue menor que 0.015, por lo que la respuesta matemáticamente correcta al redondearlo a 2 decimales es 0.01. Por supuesto, 0.01 no es exactamente representable como un doble, pero esperamos que nuestro resultado sea el doble más cercano a 0.01. Usar snprintf
nos da ese resultado, pero usar round(100 * x) / 100
nos da 0.02, lo cual está mal. ¿Por qué? Porque 100 * x
nos da exactamente 1.5 como resultado. Multiplicar por 100 cambia la dirección correcta para redondear.
Para ilustrar el segundo tipo de error - el resultado a veces equivocarse debido a * 100
y / 100
no ser verdaderamente inversas entre sí - podemos hacer un ejercicio similar con un número muy grande:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Nuestro número ahora ni siquiera tiene una parte fraccional; es un valor entero, solo almacenado con tipo double
. Entonces, el resultado después de redondearlo debería ser el mismo número con el que comenzamos, ¿verdad?
Si ejecuta el programa anterior, verá:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Ups Nuestro snprintf
método devuelve el resultado correcto nuevamente, pero el enfoque de multiplicar, redondear y luego dividir falla. Esto se debe a que el valor matemáticamente correcto de 8631192423766613.0 * 100
, 863119242376661300.0
no es exactamente representable como un doble; el valor más cercano es 863119242376661248.0
. Cuando divide eso por 100, obtiene 8631192423766612.0
un número diferente al que comenzó.
Esperemos que sea una demostración suficiente de que el uso roundf
para redondear a un número de decimales está roto, y que debería usar snprintf
en su lugar. Si eso te parece un truco horrible, quizás te tranquilice saber que es básicamente lo que hace CPython .
Uso float roundf(float x)
.
"Las funciones de redondeo redondean su argumento al valor entero más cercano en formato de punto flotante, redondeando a la mitad los casos desde cero, independientemente de la dirección de redondeo actual". C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Dependiendo de su float
implementación, los números que pueden parecer intermedios no lo son. como punto flotante es típicamente orientado a base 2. Además, redondear con precisión al más cercano 0.01
en todos los casos "intermedios" es el más difícil.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
Aunque "1.115" está "a mitad de camino" entre 1.11 y 1.12, cuando se convierte a float
, el valor es 1.115000009537...
y ya no es "a mitad de camino", sino más cercano a 1.12 y se redondea al más cercano float
de1.120000004768...
"1.125" es "mitad de camino" entre 1.12 y 1.13, cuando se convierte a float
, el valor es exactamente 1.125
y es "mitad de camino". Se redondea hacia 1.13 debido a lazos para gobernar incluso y se redondea al más cercano float
de1.129999995232...
Aunque "1.135" está "a mitad de camino" entre 1.13 y 1.14, cuando se convierte a float
, el valor es 1.134999990463...
y ya no es "a mitad de camino", sino más cercano a 1.13 y se redondea al más cercano float
de1.129999995232...
Si se usa el código
y = roundf(x*100.0f)/100.0f;
Aunque "1,135" es "a medio camino" entre 1,13 y 1,14, cuando se convierte a float
, el valor es 1.134999990463...
y ya no es "medio-way", pero más cerca de 1.13 pero incorrectamente rondas a float
de 1.139999985695...
debido a la más limitada precisión de float
vs. double
. Este valor incorrecto puede verse como correcto, dependiendo de los objetivos de codificación.
Hice esta macro para redondear números flotantes. Agréguelo en su encabezado / ser de archivo
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Aquí hay un ejemplo:
float x = ROUNDF(3.141592, 100)
x es igual a 3.14 :)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
Aquí n
está el número de decimales
ejemplo:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
es enorme 3) el extraño if
/ else
bloque donde haces exactamente lo mismo en cada rama y 4) el uso excesivamente complicado de sprintf
para construir el especificador de formato para una segunda sprintf
llamada; es más simple usar .*
y pasar el doble valor y el número de decimales como argumentos a la misma sprintf
llamada.
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
Permítanme primero intentar justificar mi razón para agregar otra respuesta a esta pregunta. En un mundo ideal, el redondeo no es realmente un gran problema. Sin embargo, en sistemas reales, es posible que deba lidiar con varios problemas que pueden resultar en redondeo que puede no ser lo que espera. Por ejemplo, es posible que esté realizando cálculos financieros donde los resultados finales se redondean y se muestran a los usuarios como 2 lugares decimales; estos mismos valores se almacenan con precisión fija en una base de datos que puede incluir más de 2 decimales (por varias razones; no hay un número óptimo de lugares para guardar ... depende de situaciones específicas que cada sistema debe soportar, por ejemplo, artículos pequeños cuyos precios son fracciones de un centavo por unidad); y, cálculos de coma flotante realizados en valores donde los resultados son más / menos épsilon. He estado enfrentando estos problemas y desarrollando mi propia estrategia a lo largo de los años. No afirmaré que me he enfrentado a todos los escenarios o que tengo la mejor respuesta, pero a continuación hay un ejemplo de mi enfoque hasta ahora que supera estos problemas:
Suponga que 6 decimales se consideran precisión suficiente para los cálculos de flotantes / dobles (una decisión arbitraria para la aplicación específica), utilizando la siguiente función / método de redondeo:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
El redondeo a 2 decimales para la presentación de un resultado se puede realizar como:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Para val = 6.825
, el resultado es 6.83
como se esperaba.
Para val = 6.824999
, el resultado es 6.82
. Aquí la suposición es que el cálculo resultó exactamente 6.824999
y el séptimo lugar decimal es cero.
Para val = 6.8249999
, el resultado es 6.83
. El séptimo lugar decimal 9
en este caso hace que la Round(val,6)
función dé el resultado esperado. Para este caso, podría haber cualquier número de 9
s finales .
Para val = 6.824999499999
, el resultado es 6.83
. Redondeando al octavo lugar decimal como primer paso, es decir Round(val,8)
, se ocupa del único caso desagradable por el cual un resultado de coma flotante calculado se calcula 6.8249995
, pero se representa internamente como 6.824999499999...
.
Finalmente, el ejemplo de la pregunta ... val = 37.777779
da como resultado 37.78
.
Este enfoque podría generalizarse aún más como:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
donde N es la precisión que se debe mantener para todos los cálculos intermedios en flotadores / dobles. Esto también funciona en valores negativos. No sé si este enfoque es matemáticamente correcto para todas las posibilidades.
Código C simple para redondear un número:
float n = 3.56;
printf("%.f", n);
Esto generará:
4
... o puedes hacerlo a la antigua usanza sin ninguna biblioteca:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Eso, por supuesto, si desea eliminar la información adicional del número.
esta función toma el número y la precisión y devuelve el número redondeado
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
convierte el número de coma flotante en int desplazando el punto a la izquierda y verificando la condición mayor de cinco.
float
(ydouble
) no son coma flotante decimal, son coma flotante binaria, por lo que redondear a posiciones decimales no tiene sentido. Sin embargo, puede redondear la salida.