¿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 .
doublestambién de alguna manera? No parece hacer el trabajo que quiero :( (usando floory ceil).
Usando % .2f en printf. Solo imprime 2 puntos decimales.
Ejemplo:
printf("%.2f", 37.777779);
Salida:
37.77
floatrango, ya que val * 100podrí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 floata otro floatporque el redondeado floatpuede 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 floatmediante una función de cadena de formato.
floata 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 myDoublecon nlugares 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 printffamilia de funciones para esto. Incluso si desea obtener el valor como flotante, es mejor usarlo snprintfpara 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 snprintfnos da ese resultado, pero usar round(100 * x) / 100nos da 0.02, lo cual está mal. ¿Por qué? Porque 100 * xnos 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 * 100y / 100no 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 snprintfmé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.0no es exactamente representable como un doble; el valor más cercano es 863119242376661248.0. Cuando divide eso por 100, obtiene 8631192423766612.0un número diferente al que comenzó.
Esperemos que sea una demostración suficiente de que el uso roundfpara redondear a un número de decimales está roto, y que debería usar snprintfen 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 floatimplementació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.01en 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 floatde1.120000004768...
"1.125" es "mitad de camino" entre 1.12 y 1.13, cuando se convierte a float, el valor es exactamente 1.125y es "mitad de camino". Se redondea hacia 1.13 debido a lazos para gobernar incluso y se redondea al más cercano floatde1.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 floatde1.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 floatde 1.139999985695...debido a la más limitada precisión de floatvs. 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í nestá 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
dvales enorme 3) el extraño if/ elsebloque donde haces exactamente lo mismo en cada rama y 4) el uso excesivamente complicado de sprintfpara construir el especificador de formato para una segunda sprintfllamada; es más simple usar .*y pasar el doble valor y el número de decimales como argumentos a la misma sprintfllamada.
#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.83como se esperaba.
Para val = 6.824999, el resultado es 6.82. Aquí la suposición es que el cálculo resultó exactamente 6.824999y el séptimo lugar decimal es cero.
Para val = 6.8249999, el resultado es 6.83. El séptimo lugar decimal 9en este caso hace que la Round(val,6)función dé el resultado esperado. Para este caso, podría haber cualquier número de 9s 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.777779da 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.