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 mit

    Integer.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 signaling NaN 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 mit copysign abfragen (und erzeugen).
    http://en.cppreference.com/w/cpp/numeric/math/copysign




Log in to reply