2012-05-25 24 views
24

Mi aplicación necesita convertir valores dobles a char * para escribir en una tubería que solo acepta caracteres. Las formas habituales de hacerlo son utilizando la función sprintf() o utilizando ostringstream desde el archivo de encabezado iomanip.h.Conversión de doble a char * en C++ con alto rendimiento

Resulta que ambos tienen muy mal rendimiento. Y mi aplicación necesita hacer esta conversión tan a menudo que se convierte en el principal cuello de botella.

¿Hay alguna otra función que pueda usar? ¿Qué lógica puedo usar para escribir una función de conversión eficiente? Lo único que he logrado llegar hasta ahora es sacar cada dígito individual usando operaciones de división y mod, y agregar estos dígitos a un char * para obtener el doble valor completo. Sin embargo, esto no parece ser un buen enfoque, y probablemente tendrá un mal rendimiento.

Gracias de antemano por su opinión.

EDITAR: Existe cierta confusión sobre cómo se utilizará el carácter *. El char * será un argumento para la función fwrite que escribe en una tubería.

+4

¿Qué quiere decir con "tubería acepta solo caracteres"? ¿Puedes enviar/escribir bytes? ¿El formato en el que los datos se destinan a este asunto de la tubería (debe ser legible para el ser humano) o lo vuelves a convertir en otro lado? – dbrank0

+2

Lea esto: http://stackoverflow.com/questions/3173056/why-does-dtoa-c-contain-so-much-code – nhahtdh

+0

¿Por qué no bitcast a un int y codifica eso en hexadecimal y envía eso (luego en el otro lado convierte de nuevo) –

Respuesta

17

Si desea imprimir cualquier número que el tipo doble pueda admitir, utilice la biblioteca disponible para realizar el trabajo. Le ahorra cordura: Why does "dtoa.c" contain so much code?

Si desea imprimir un subconjunto de números en tipo doble. Por ejemplo, hasta 4 dígitos después del punto decimal, y no más de 5 dígitos antes del punto decimal, entonces puede redondear el número y convertirlo a tipo int, antes de imprimirlo usando división y mod. Puedo confirmar el rendimiento de este método.


EDIT: Si propósito original es enviar los datos para la comunicación, a continuación, enviar el formato binario de doble será el método más rápido y más preciso (sin posible pérdida de precisión debido a la conversión). La forma de hacer esto se explica en otras respuestas.

+0

+1: si el OP sabe algo sobre el rango de flotantes presente, la conversión de entero escalado es probablemente la mejor optimización. – wallyk

+6

Quiere serializar los contenidos de doble. No hay necesidad de representarlo. – RedX

7

si quiere leer bytes de datos por byte. utilice esta técnica

double dbl = 2222; 
char* ptr = (char*)(&dbl); 

esto devolverá el byte más bajo de dbl. y ptr ++ se referirá al segundo byte de dbl.

+1

No es tipo de fundición. La pregunta es sobre el valor de impresión del doble. – nhahtdh

+1

necesita enviarlo por caracteres, y creo que esto se puede hacer con esto ya que tiene los bytes de doble variable y se puede combinar en cuando se reciben todos los bytes en 2nd End – Saad

+0

Puede que tenga razón. La pregunta no es tan clara sobre el propósito de la conversión. – nhahtdh

2

Algunos sistemas ofrecen funciones de conversión dtostre y dtostrf - podría valer la comparación. Puede mirar el código fuente sprintf() (por ejemplo, la versión GNU) para obtener ideas, seleccionar el formato %e, %f y/o %g según lo desee, evitando el paso de interpretación de cadenas de formato e incluso haciéndolo ineludible, y sintonizado con el rendimiento - es posible que ser capaz de eliminar la carcasa especial para NaN, infinito y otros valores si sabe que no tiene que manejarlos.

1

El uso de ftoa será ligeramente mejor que sprintf ya que esto es lo que está utilizando internamente. Véase la pregunta relacionada here también a ver cómo se implementa ftoa en su fuente de la biblioteca y ver si se puede mejorar en él para sus situaciones específicas.

Parece ftoa no es estándar en todos, mi mal. He aquí una discussion showing an implementation que pretende ser mucho más rápido que sprintf. Tendría que crear perfiles en sus propios entornos de destino e implementar con el doble en lugar de flotar.

+0

¿Está ftoa (para flotación) o dtoa (para doble) en la especificación en cualquier versión? – nhahtdh

+0

Después de un control rápido, aparentemente no! Voy a rebuscar un poco, ya que ciertamente lo he visto cuando paso por algunas bibliotecas. Posiblemente no sea tan estándar como pensaba. –

8

Puede utilizar el std :: ostream write y std :: istream read métodos con cualquier tipo de datos que sólo tiene que reinterpret_cast los datos como un puntero char:

double value = 5.0; 
std::ostream os; 
//... 
os.write(reinterpret_cast<const char*>(&value), sizeof(value)); 
//.. 
std::istream is; 
is.read(reinterpret_cast<char*>(&value), sizeof(value)); 
+0

No creo que esto sea rápido: Todavía +1 por su esfuerzo ...! –

2

La parte lenta de sprintf en su el sistema no necesariamente está convirtiendo el doble, sino analizando la cadena de formato. Esa podría ser una buena noticia para usted, ya que es algo que podría optimizar.

Además, intente documentar todo el conocimiento que tiene sobre el alcance, la precisión y la naturaleza de los valores double que debe procesar, y úselos para desarrollar un algoritmo especial.

Suponiendo sus entradas nunca son números anormales, utilizan una precisión fija conocida y precisión, un resultado relativamente alto rendimiento puede tener este aspecto:

itoa((int)((f + 0.00001) * 10000)) 

Sin embargo, el sprintf y ostream se acerca ya está al tanto de son las únicas soluciones que son completamente generales y portátiles.

+1

El punto decimal no se imprimirá. – nhahtdh

+0

@nhahtdh - Eso es cierto.Eliminaré mi respuesta por completo a menos que vea ediciones de preguntas que aclararían el propósito exacto de la tubería de salida. Al final, el dumping de los flotadores en el binario podría ser una solución excelente (respuesta de Casey), o podría ser inutilizable. –

+0

Aunque no es directamente aplicable, su respuesta me da pistas sobre cómo resolver el problema. ¡Gracias! –

2
/* 

_ecvt_s Converts a double number to a string. 

Syntax: 

errno_t _ecvt_s( 
    char * _Buffer, 
    size_t _SizeInBytes, 
    double _Value, 
    int _Count, 
    int *_Dec, 
    int *_Sign 
); 

[out] _Buffer 
Filled with the pointer to the string of digits, the result of the conversion. 

[in] _SizeInBytes 
Size of the buffer in bytes. 

[in] _Value 
Number to be converted. 

[in] _Count 
Number of digits stored. 

[out] _Dec 
Stored decimal-point position. 

[out] _Sign 
Sign of the converted number. 


*/ 


#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 

... 
char *buf = (char*) malloc(_CVTBUFSIZE); 
int decimal; 
int sign; 
int err; 


err = _ecvt_s(buf, _CVTBUFSIZE, 1.2, 5, &decimal, &sign); 

if (err != 0) { 
// implement error handling 
} 
else printf("Converted value: %s\n", buf); 

... 

Espero que esto ayude.

3

¿Tiene el control de ambos extremos de la tubería? ¿Estás tratando de pasar el doble del túnel o necesitas una representación de texto válida del doble?

Si solo está haciendo un túnel y el tubo está 8 bits limpio, utilice una de las respuestas anteriores.

Si necesita una cadena, use una de las otras respuestas anteriores.

Si su problema es que el tubo tiene solo 7 bits de ancho, conviértalo a radix 64 en escritura y viceversa en lectura.