Umwandlung von JAVA double in C++ double
-
Hallo,
folgendes Szenario: Ich habe in C++ einen TCP Server laufen und in JAVA einen TCP client. Jetzt schickt der JAVA client dem server Daten in Form von integers und doubles. In C++ kommen diese jedoch erstmal als purer Spreicher an:uint8* buffer;//z.B. 8 byte lang für ein double
Wenn ich jetzt folgendes mache, kommen jedoch falsche Zahlen heraus:
int32 i; std::memcpy(&i, buffer, 4);
Mittlerweile habe ich das für integer unsauber gelöst:
int32 bitsToInt(uint8* bytes) { int32 i; if (bytes[0] < 128) { i = bytes[0]; i = i << 8; i += bytes[1]; i = i << 8; i += bytes[2]; i = i << 8; i += bytes[3]; } else { i = bytes[0]-128; i = i << 8; i += bytes[1]; i = i << 8; i += bytes[2]; i = i << 8; i += bytes[3]; i -= 2147483648; } return i; }
Für IEEE 754 double bekomme ich das so jedoch irgendwie nicht hin und es ist mir auch zu blöd.
Die Frage ist also wieso funktioniert memcpy oder ein einfacher cast nicht?
Ich freue mich schon auf eure Antworten.
msg lonol15
-
Du solltest lieber zu einem anderen Format zurückgreifen, wie z.B JSON oder XML, als reine Bytes zu verwenden, wenn du zwischen Java und C++ kommunizierst. Das bereitet nämlich weniger Kopfschmerzen.
Falls es anders nicht zu lösen ist, bräuchte man noch deinen JAVA Code.
-
Das Problem mit XML oder JSON wäre ein erhöhter overhead und bei meinem Problem geht es darum teilweise sechzig mal die Sekunde große Datenmengen zu übertragen.
Ich mache das mit einem DataoutputStream:
DataOutputStream outputStream; ... Translation tr = data.get(i).read(); outputStream.writeInt(i); outputStream.writeInt(data.get(i).getParentID()); outputStream.writeDouble(tr.getX()); outputStream.writeDouble(tr.getY()); outputStream.writeDouble(tr.getZ()); outputStream.writeDouble(tr.getA()); outputStream.writeDouble(tr.getB()); outputStream.writeDouble(tr.getC()); ... outputStream.flush();
-
Der DataOutputStream dreht die Bytes um, er schreibt in Big Endian. Siehe https://en.wikipedia.org/wiki/Endianness. Wie floats und doubles geschrieben werden, kannst du vermutlich der Dokumentation entnehmen.
-
d.h. deine "int32 bitsToInt(uint8* bytes)" ist der total falsche und unnötige Ansatz
du musst nur high und low bytes verdrehen, bei float und double koennte es genau so sein
-
Für Integer sollte ntohl die Bytes richtig drehen.
-
Danke für die Antworten, dann werde ich erstmal versuchen das auf der JAVA Seite mit einem OutpuStrem zu beheben.
-
Also ich habe das jetzt auf der JAVA Seite erfolgreich gelöst:
Den DataOutputStream kann man behalten, wenn man davor die bytes umdreht. Bei Integern geht das mitInteger.reverseBytes(int x)
. Bei double wird es schon ein bisschen umständlicher. Da habe ich mir eine kleine Hilfsfunktion geschrieben:
public static double reverse(double in){ return Double.longBitsToDouble(Long.reverseBytes(Double.doubleToLongBits(in))); }
-
du könntest ja den float Wert aufteilen auf: Vorzeichen, Mantisse, Exponent.
Die drei Werte speicherst du getrennt (z.B. als int oder als string) und lädst auch wieder getrennt rein - dann musst du nicht auf Bitebene rumbasteln.
-
mnmnbmn schrieb:
du könntest ja den float Wert aufteilen auf: Vorzeichen, Mantisse, Exponent ..... dann musst du nicht auf Bitebene rumbasteln.
Ah ja ... und wie teilt man das auf, ohne auf Bitebene rumzubasteln?
-
lonol15 schrieb:
public static double reverse(double in){ return Double.longBitsToDouble(Long.reverseBytes(Double.doubleToLongBits(in))); }
Keine gute Idee.
Was wenn die falsche Byte-Reihenfolge einen signalingNaN
darstellt?
-
Belli schrieb:
mnmnbmn schrieb:
du könntest ja den float Wert aufteilen auf: Vorzeichen, Mantisse, Exponent ..... dann musst du nicht auf Bitebene rumbasteln.
Ah ja ... und wie teilt man das auf, ohne auf Bitebene rumzubasteln?
z.B. mit
frexp
& Co.
http://www.cplusplus.com/reference/cmath/frexp/Die Mantisse bekommt man dabei wieder als
double
.
Macht aber nix, da man dann weiss dass die sich irgendwo im Bereich [0.5 ... 1) bewegt.
Bzw. wenn negativ dann halt (-1 ... -0.5].D.h. man kann erstmal das Vorzeichen notieren und rausrechnen und dann einfach so lange
* verdoppeln
* wenn >= 1 dann 1 abziehen
* wiederholen
bis der Wert 0 ist.
Bzw. einfach N mal wiederholen (mit N = grösste zu erwartende Mantissenbreite).
Und bei jedem Durchlauf ne 1 bzw. 0 notieren, je nachdem ob man 1 abgezogen hat oder nicht.Und den Exponenten bekommt man von
frexp
ja sowieso schon als Integer.Alles ohne Bitfummelei.
Die umgekehrte Richtung sollte trivial sein.Dann fehlen bloss noch die Spezialwerte (NaNs, INFs, ...).
Vorzeichen für 0 und die Spezielwerte kann man mitcopysign
abfragen (und erzeugen).
http://en.cppreference.com/w/cpp/numeric/math/copysign
-
ist google so schwer?
http://www.java2s.com/Code/Java/Language-Basics/Utilityforbyteswappingofalljavadatatypes.htm