2012-05-25 13 views
24

Meine Anwendung muss doppelte Werte in char * konvertieren, um in eine Pipe zu schreiben, die nur Zeichen akzeptiert. Die üblichen Methoden hierfür sind die Verwendung der Funktion sprintf() oder die Verwendung von ostringstream aus der Headerdatei iomanip.h.Konvertieren von Double in char * in C++ mit hoher Performance

Stellt sich heraus, beide haben wirklich schlechte Leistung. Und meine Anwendung muss diese Konvertierung so oft durchführen, dass sie zum primären Engpass wird.

Gibt es eine andere Funktion, die ich verwenden könnte? Welche Logik kann ich verwenden, um eine effiziente Konvertierungsfunktion zu schreiben? Das Einzige, was mir bisher gelungen ist, ist, jede einzelne Ziffer mit Hilfe von Divisions- und Mod-Operationen herauszufiltern und diese Ziffern an ein char * anzuhängen, um den gesamten doppelten Wert zu erhalten. Dies scheint jedoch kein guter Ansatz zu sein und wird wahrscheinlich selbst eine schlechte Leistung haben.

Vielen Dank im Voraus für Ihre Gedanken.

EDIT: Es gibt einige Verwirrung darüber, wie das char * verwendet wird. Das Zeichen char * wird ein Argument für die Funktion fwrite sein, die in eine Pipe schreibt.

+4

Was meinen Sie mit "Pipe akzeptiert nur Zeichen"? Können Sie Bytes senden/schreiben? Ist das Format, in dem die Daten in diesen Bereich fallen (muss es für Menschen lesbar sein), oder konvertieren Sie es auf der anderen Seite zurück? – dbrank0

+2

Lesen Sie hierzu: http://StackOverflow.com/Questions/3173056/Why-Does-Dtoa-C-Contain-So-Much-Code – nhahtdh

+0

Warum nicht bitcast zu einem int und kodieren Sie das in hex und senden Sie das (dann auf der andere Seite konvertieren zurück) –

Antwort

17

Wenn Sie eine beliebige Zahl drucken möchten, die vom Doppeltyp unterstützt wird, verwenden Sie die entsprechende Bibliothek, um die Aufgabe zu erledigen. Es speichert Ihre Gesundheit: Why does "dtoa.c" contain so much code?

Wenn Sie eine Teilmenge von Zahlen im Doppeltyp drucken möchten. Zum Beispiel, bis zu 4 Ziffern nach dem Komma und nicht mehr als 5 Ziffern vor dem Dezimalpunkt, dann können Sie die Zahl runden und in den Int-Typ konvertieren, bevor Sie sie mit Division und Mod ausdrucken. Ich kann die Leistung dieser Methode bestätigen.


EDIT: Wenn Sie ursprünglichen Zweck ist es, die Daten für die Kommunikation zu senden, dann die binäre Form von Doppel senden, wird die schnellste und genaueste Methode (aufgrund der Umwandlung nicht möglich Genauigkeitsverlust) sein. Die Art, dies zu tun, wird in anderen Antworten erklärt.

+0

+1: Wenn das OP etwas über den Bereich der vorhandenen Floats weiß, ist die skalierte Integer-Konvertierung wahrscheinlich die beste Optimierung. – wallyk

+6

Er möchte den Inhalt von double serialisieren. Es gibt keine Notwendigkeit, es darzustellen. – RedX

7

wenn Sie Daten byteweise lesen möchten. Verwenden Sie diese Technik

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

Dies wird niedrigsten Byte von dbl zurückgeben. und ptr ++ beziehen sich auf das zweite Byte von dbl.

+1

Es ist kein Typ Gießen. Die Frage ist, den Wert des Doppelten auszudrucken. – nhahtdh

+1

er muss es durch Zeichen senden, und ich denke, dass dies durch diese getan werden kann, da Sie die Bytes der doppelten Variable haben und kombiniert werden können, wenn alle Bytes am 2. Ende empfangen werden – Saad

+0

Sie könnten Recht haben. Die Frage ist nicht so klar über den Zweck der Umwandlung. – nhahtdh

2

Einige Systeme bieten dtostre und dtostrf Konvertierungsfunktionen - vielleicht lohnt sich ein Benchmarking. Sie können sprintf() Quellcode (zB GNU-Version) für Ideen, Abholung der %e, %f und/oder %g Formatierung wie gewünscht, Vermeidung der Formatzeichenfolge Interpretationsschritt und sogar macht es einlining und Performance-tune nach Geschmack - Sie könnten in der Lage sein, Spezialgehäuse für NaN, Unendlich und andere Werte zu entfernen, wenn Sie wissen, dass Sie nicht damit umgehen müssen.

1

Die Verwendung von ftoa wird etwas besser als sprintf sein, da dies intern verwendet wird. Siehe verwandte Frage here Sehen Sie sich auch an, wie ftoa in Ihrer Bibliotheksquelle implementiert ist und ob Sie es für Ihre spezifischen Szenarien verbessern können.

Es scheint ftoa ist überhaupt nicht Standard, mein schlechtes. Hier ist eine discussion showing an implementation, die behauptet, dass sprintf viel schneller sein. Sie müssten beide in Ihren eigenen Zielumgebungen profilieren und mit Double statt Float implementieren.

+0

Ist ftoa (für float) oder dtoa (für double) in der Spezifikation in jeder Version? – nhahtdh

+0

Nach einer kurzen Überprüfung, anscheinend nicht! Ich werde ein wenig herumkramen, wie ich es schon gesehen habe, wenn ich einzelne Bibliotheken durchtrete. Möglicherweise nicht so Standard wie ich dachte. –

8

Sie die std verwenden können :: ostream write und std :: istream read Methoden mit einem beliebigen Datentyp Sie müssen nur die Daten als char Zeiger reinterpret_cast:

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

ich glaube nicht, dass das schnell wäre: Noch +1 für deine Mühe ..! –

2

Der langsame Teil sprintf auf Das System konvertiert nicht unbedingt das Double, sondern analysiert die Formatzeichenfolge. Das könnte eine gute Nachricht für Sie sein, da Sie etwas optimieren können.

Versuchen Sie außerdem, Ihr gesamtes Wissen über den Bereich, die Genauigkeit und die Art der double Werte, die Sie verarbeiten müssen, zu dokumentieren, und verwenden Sie es, um einen speziellen Algorithmus zu entwickeln.

Angenommen, Ihre Eingaben sind nie subnormale Zahlen, verwenden Sie eine bekannte feste Präzision und Genauigkeit, eine relativ hohe Leistung Ergebnis wie folgt aussehen:

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

jedoch die sprintf und ostream Ansätze, die Sie sind bereits bekannt sind die einzigen Lösungen, die komplett allgemein und portabel sind.

+1

Der Dezimalpunkt wird nicht gedruckt. – nhahtdh

+0

@nhahtdh - Das stimmt.Ich lösche meine Antwort vollständig, es sei denn, ich sehe Fragen, die den genauen Zweck des Ausgabepipes verdeutlichen. Am Ende könnte es sein, dass die Floats in der Binärdatei eine ausgezeichnete Lösung darstellen (Caseys Antwort), oder sie könnte unbrauchbar sein. –

+0

Obwohl nicht direkt anwendbar, gibt Ihre Antwort einige Hinweise, wie Sie das Problem lösen können. Vielen Dank! –

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); 

... 

Hoffe das hilft.

3

Haben Sie die Kontrolle über beide Rohrenden? Versuchen Sie nur, Doubles zu tunneln oder benötigen Sie eine gültige Textdarstellung des Double?

Wenn Sie nur tunneln und das Rohr ist 8 Bit sauber dann verwenden Sie eine der Antworten oben.

Wenn Sie eine Zeichenfolge benötigen, verwenden Sie eine der anderen Antworten oben.

Wenn Ihr Problem ist, dass die Pipe nur 7 Bits breit ist, dann konvertieren Sie auf radix 64 auf schreiben und wieder auf lesen.