Truncar un valor decimal en C ++
Pregunta
¿Cuál es la forma más fácil de truncar una variable C ++ float
que tiene un valor de 0.6000002 a un valor de 0.6000 y almacenarla nuevamente en la variable?
Solución
Primero es importante saber que los números de coma flotante son aproximados. Consulte el enlace proporcionado por @Greg Hewgill para comprender por qué este problema no es completamente solucionable.
Pero aquí hay un par de soluciones al problema que probablemente satisfarán su necesidad:
Probablemente el mejor método pero menos eficiente:
char sz[64];
double lf = 0.600000002;
sprintf(sz, "%.4lf\n", lf); //sz contains 0.6000
double lf2 = atof(sz);
//lf == 0.600000002;
//lf2 == 0.6000
printf("%.4lf", lf2); //print 0.6000
La forma más eficiente, pero probablemente menos precisa:
double lf = 0.600000002;
int iSigned = lf > 0? 1: -1;
unsigned int uiTemp = (lf*pow(10, 4)) * iSigned; //Note I'm using unsigned int so that I can increase the precision of the truncate
lf = (((double)uiTemp)/pow(10,4) * iSigned);
Otros consejos
Una buena referencia para por qué esto sucede se puede encontrar en Lo que todo informático debe saber sobre la aritmética de coma flotante por David Goldberg.
Siendo realistas, eso no es posible. No es una limitación de C ++, sino la forma en que funciona el punto flotante. Para muchos valores no hay representaciones precisas, por lo que no puede simplemente truncar a una cantidad de dígitos.
Puede truncar al imprimir utilizando cadenas de formato printf.
Si realmente necesita poder almacenar solo un número limitado de dígitos, le sugiero que utilice un tipo de datos de precisión fija en su lugar.
Creo que la pregunta que debería hacerse aquí es: ¿Por qué lo necesitas truncado?
Si es para comparar valores, quizás debería considerar usar la prueba epsilon. (con un valor de tolerancia adicional, en su caso, ya que parece ser mucho más grande que el epsilon generalmente aceptado).
Si solo desea imprimirlo como 0.6000, use los métodos que otros han sugerido.
roundf(myfloat * powf(10, numDigits)) / powf(10, numDigits);
Por ejemplo, en su caso está truncando tres dígitos (numDigits). Usarías:
roundf(0.6000002 * 1000) / 1000
// And thus:
roundf(600.0002) / 1000
600 / 1000
0.6
(Probablemente almacenarías el resultado de powf en algún lugar, ya que lo estás usando dos veces).
Debido a cómo los flotadores se almacenan normalmente en las computadoras, probablemente habrá imprecisiones. Sin embargo, eso es lo que obtienes por usar flotadores.
Use esto:
floor(0.6000002*10000)/10000
Para C ++ 11 puede usar std::round
definido en el encabezado <cmath>
:
auto trunc_value = std::round(value_to_trunc * 10000) / 10000;
Aquí hay una función que utiliza los consejos de otras respuestas y un ejemplo de su uso:
#include <iostream>
#include <cmath>
static void Truncate(double& d, unsigned int numberOfDecimalsToKeep);
int main(int, char*[])
{
double a = 1.23456789;
unsigned int numDigits = 3;
std::cout << a << std::endl;
Truncate(a,3);
std::cout << a << std::endl;
return 0;
}
void Truncate(double& d, unsigned int numberOfDecimalsToKeep)
{
d = roundf(d * powf(10, numberOfDecimalsToKeep)) / powf(10, numberOfDecimalsToKeep);
}
Similar a otras respuestas, PERO no debes olvidar que la ronda, el piso y el trunc son diferentes por definición. Consulte la definición y el ejemplo de salida de lo siguiente:
http://www.cplusplus.com/reference/cmath/trunc/
En este caso, necesitamos trucar con una precisión de 4 decimales y deshacernos de decimales no significativos:
trunc(valueToTrunc*10000)/10000
o
value = (double)((int)(valueToTrunc*10000))/(double)10000