Red de conocimiento informático - Material del sitio web - Cómo formatear números de punto flotante en lenguaje C

Cómo formatear números de punto flotante en lenguaje C

Si hay una función que acepta un parámetro doble largo y convierte el parámetro en una cadena, la cadena resultante debe conservar dos decimales. Por ejemplo, el valor de punto flotante 123.45678 debería generar "123.45" como este. Cadena. A primera vista, esto puede parecer un problema de programación trivial, pero para que sea útil en la práctica, la función debe diseñarse con cierta flexibilidad para permitir que quien llama especifique el número de decimales. Además, la función también debería poder manejar varias excepciones, como números enteros como 123.0 o 123.

Antes de comenzar, echemos un vistazo a dos "mantras" para escribir código C "elegante":

Mantra 1: Siempre que necesite formatear un valor, debe convertirlo a una cuerda primero. Esto asegura que cada dígito ocupe exactamente un carácter.

Cadena 4

"Mantra" 2: Cuando necesite convertir a una cadena, utilice la biblioteca lt;sstreamgt;.

La interfaz de la función de conversión es muy simple: el primer parámetro es el valor que se va a formatear; el segundo parámetro representa el número de decimales que se muestran después del punto decimal y debe tener un valor predeterminado; el valor de retorno es una cadena Tipo:

cadena do_fraction (valor doble largo, int decplaces=3);

Tenga en cuenta que el número de decimales representado por el segundo parámetro incluye el punto decimal , por lo tanto, dos decimales. Se requiere que el valor predeterminado sea 3.

Problemas de precisión

Por supuesto, el primer paso es convertir el valor doble largo en una cadena, que es Se hace fácilmente usando la biblioteca C estándar lt;sstreamgt; Sin embargo, se debe tener en cuenta una cosa. Por algunas razones, la precisión predeterminada de los objetos de secuencia de cadenas es 6 y muchos programadores entienden erróneamente que la "precisión" es el número de decimales. La precisión debe referirse a todos los dígitos. Por lo tanto, el número 1234,56 se puede representar de forma segura con la precisión predeterminada de 6, pero 12345,67 se truncará a 12345,6. De esta manera, si tiene un número muy grande, como 1234567,8, su resultado se convertirá silenciosamente a notación científica: 1,23457. e 06, que obviamente no es lo que queremos. Para evitar este tipo de problemas, la precisión predeterminada debe establecerse al máximo antes de iniciar la conversión.

La cadena 8 es para obtener el número máximo de dígitos que long double puede representar, puedes usar lt; limitsgt; string do_fraction(long double value, int decplaces=3)

{

int prec=numeric_limitslt; long doublegt; ::digits10; // 18

ostringstream out; /Anular la precisión predeterminada

outlt;lt;value;

string str= out.str(); //El valor de cadena tomado de la secuencia ahora se almacena en str, esperando para formatear.

La posición del punto decimal

Para formatear, primero determine la posición del punto decimal. Si hay más lugares decimales que decplaces, do_fraction() eliminará el exceso.

Para localizar el lugar decimal, puede utilizar string::find(). En el algoritmo STL, se utiliza una constante para representar "valor no encontrado". En una cadena, esta constante es string:: npos: ? String 4

char DECIMAL_POINT='.'; // El uso europeo es ','

size_t n=str.find(DECIMAL_POINT);

if ((n!=string::npos)//¿Hay un punto decimal?

{

//Comprueba el número de decimales

}

Si no hay punto decimal, la función devuelve la cadena directamente. De lo contrario, la función continuará verificando si hay más lugares decimales que decplaces. Si es así, la parte decimal se truncará:? size_t n=str.find(DECIMAL_POINT);

if ((n!=string::npos)//¿Hay un punto decimal?

amp; (str.size( )gt; n decplaces)) //Al menos lo siguiente ¿Hay bits de decplaces

//Escribe nul después de los dígitos de decplaces decimales

str[n decplaces]='\ 0';

La última línea Para cubrir los lugares decimales adicionales, utiliza la constante \0 para truncar la cadena. Tenga en cuenta que los datos del objeto de cadena pueden contener caracteres nulos y la longitud real de la misma. La cadena está determinada por el valor de retorno de size(). Por lo tanto, no se puede suponer que la cadena se haya formateado correctamente. En otras palabras, si el valor original en str es "123.4567", después de insertar la constante \0. "123.45\07". Para reducir str a "123.45" ", generalmente puede utilizar el método de intercambio automático:

str.swap(string(str.c_str())); // Eliminar los caracteres adicionales después de nul

Entonces su principio ¿Qué es? La función string::c_str() devuelve un carácter constante * que representa este objeto de cadena, y este valor se usa como valor de inicialización de una cadena temporal. objeto, y luego el objeto temporal se usa como parámetro str.swap(), swap() asignará el valor "123.45" a str. Algunos compiladores más antiguos no admiten parámetros de plantilla predeterminados y es posible que no permitan que swap() se compile. Si es así, utilice el intercambio manual en su lugar: ? string temp=str.c_str();

str=temp;

Aunque el código no es muy "hermoso", puede lograrlo. el propósito.

El siguiente es el código completo de do_fraction():

string do_fraction(long double value, int decplaces=3)

{

ostringstream out;

int prec=

numeric_limitslt; long doublegt; ::digits10; // 18

out.precision(prec); >

outlt;lt;value;

string str= out.str(); //Obtener la cadena de la secuencia

Cadena 8

size_t n =str.find(DECIMAL_POINT);

if ((n!=string::npos) //¿Hay un punto decimal?

amp;amp; (str. size()gt ; n decplaces)) //¿Hay al menos decplaces al final?

{

str[n decplaces]='\0';//Sobrescribe el primer número sobrante

}

str. swap(string(str.c_str())); //Elimina los caracteres adicionales después de nul

return str;

}

Si no lo haces desea pasar Pasar un valor devuelve un objeto de cadena, y también puede agregar un parámetro para pasar el objeto str por referencia:

void do_fraction (valor doble largo, cadena amp; str, int decplaces=3) ;

Desde un punto de vista personal, todavía prefiero dejar que el compilador haga dicha optimización. Además, usar retorno por valor también te permite usar do_fraction() de la siguiente manera:

cout lt ;lt; funct(123456789.69999001) lt;lt; '\t' lt;funct(12.011)lt;lt;endl;

Salida:

123456789.69 12.01