2009-10-20 6 views
8

Angenommen, ich habe eine Hexadezimalzahl "4072508200000000" und ich möchte die Gleitkommazahl, die es darstellt (293.03173828125000) im IEEE-754-Doppelformat sein in eine JavaScript-Variable einfügen.Konvertieren Sie eine Zeichenfolge mit einer Hexadezimaldarstellung eines IEEE-754-Double in JavaScript numerische

Ich kann mir einen Weg vorstellen, der eine Maskierung und einen Aufruf von pow() verwendet, aber gibt es eine einfachere Lösung?

Eine clientseitige Lösung wird benötigt.

Dies kann helfen. Es ist eine Website, auf der Sie eine Hex-Codierung eines IEEE-754 eingeben und eine Analyse der Mantisse und des Exponenten erhalten können.

http://babbage.cs.qc.edu/IEEE-754/64bit.html

Weil die Menschen immer dazu neigen, „warum ?,“ zu fragen ist hier, warum: Ich versuche, eine vorhandene, aber unvollständige Umsetzung der Google-Procol Puffer zu füllen (protobuf).

+0

Persönlich würde ich beginnen, indem Sie den Hex-Wert mit einem Regex in die einzelnen Stücke zerhacken. Dann würde ich diese als ganze Zahlen bewerten, und schließlich würde ich versuchen, sie wieder in einen Float zu verwandeln. Das scheint etwas zu sein, das schwierig wird, weil Sie es so machen müssen, dass die Javascript-Laufzeit keine Bits auf dem Weg verliert. – Pointy

+0

Für eine maximale Portabilität sollten Sie berücksichtigen, dass IEEE-754-Double entweder Big-Endian oder Little-Endian sein kann. Wenn Sie wissen, welche Konvention von der Hexadezimaleingabe verwendet wird, sollte eine clientseitige Lösung mit pow() portabel sein. Wenn Sie sich dazu entschließen, einen Typ-Punning-Ansatz zu verwenden, müßte zuerst die Client-Plattform-Endianz für Doubles geprüft werden. –

+0

@ Jim Lewis: Ich habe eine Flagge, die mir große oder kleine Endian sagt. – Nosredna

Antwort

10

Ich kenne keinen guten Weg. Es kann sicherlich auf die harte Art und Weise durchgeführt werden, hier ist ein einfache Genauigkeit Beispiel vollständig in JavaScript:

js> a = 0x41973333 
1100428083 
js> (a & 0x7fffff | 0x800000) * 1.0/Math.pow(2,23) * Math.pow(2, ((a>>23 & 0xff) - 127)) 
18.899999618530273 

Eine Produktion Implementierung in Betracht ziehen sollte, dass die meisten Felder magische Werte, typischerweise implementiert durch eine spezielle Auslegung für die Angabe, was wäre der größte oder kleinste gewesen. So, erkennen NaN s und Unendlichkeiten. Das obige Beispiel sollte nach Negativen suchen. (a & 0x80000000)

Update: Ok, ich habe es auch für Doppelgänger. Sie können die obige Technik nicht direkt erweitern, da die interne JS-Repräsentation ein Double ist, und nach ihrer Definition kann sie bestenfalls eine Bit-Zeichenfolge der Länge 52 verarbeiten und sie kann sich nicht um mehr als 32 verschieben.

Ok, doppelt zu tun Sie zuerst hacken als Zeichenfolge die niedrigen 8 Ziffern oder 32 Bits; Verarbeite sie mit einem separaten Objekt. Dann:

js> a = 0x40725082  
1081233538 
js> (a & 0xfffff | 0x100000) * 1.0/Math.pow(2, 52 - 32) * Math.pow(2, ((a >> 52 - 32 & 0x7ff) - 1023)) 
293.03173828125 
js> 

Ich behielt das obige Beispiel, weil es vom OP ist. Ein härterer Fall ist, wenn die niedrigen 32 Bits einen Wert haben. Hier ist die Umwandlung von 0x40725082deadbeef, ein Full-Präzision double:

js> a = 0x40725082 
1081233538 
js> b = 0xdeadbeef 
3735928559 
js> e = (a >> 52 - 32 & 0x7ff) - 1023 
8 
js> (a & 0xfffff | 0x100000) * 1.0/Math.pow(2,52-32) * Math.pow(2, e) +   
    b * 1.0/Math.pow(2, 52) * Math.pow(2, e) 
293.0319506442019 
js> 

Es gibt einige offensichtliche Unterausdrücke Sie ausklammern können, aber ich habe es so gelassen, so kann man sehen, wie es auf das Format betrifft.

+0

Mein letztes Beispiel stimmt mit Ihrer verbundenen Site für 0x40725082deadbeef überein. – DigitalRoss

+0

Sehr schön. Vielen Dank. – Nosredna

+0

Warum gibt dieser Code (zweitletztes Beispiel) '1.1125369292536007e-308' zurück, wenn a = 0? – etuardu

4

Eine schnelle Ergänzung zur DigitalRoss-Lösung, für diejenigen, die diese Seite über Google finden, wie ich es tat.

Neben den Rand Fällen für +/- Unendlich und NaN, die ich bei der Eingabe lieben würde, müssen Sie auch das Vorzeichen des Ergebnisses berücksichtigen:

s = a >> 31 ? -1 : 1 

Sie können dann schließen s in der letzten Multiplikation, um das richtige Ergebnis zu erhalten.

Ich denke, für eine Little-Endian-Lösung müssen Sie auch die Bits in a und b umkehren und tauschen Sie sie aus.

1

Der neue Typed Arrays Mechanismus ermöglicht es Ihnen, dies zu tun (und ist wahrscheinlich ein idealer Mechanismus Protokollpuffer für die Implementierung):

var buffer = new ArrayBuffer(8); 
var bytes = new Uint8Array(buffer); 
var doubles = new Float64Array(buffer); // not supported in Chrome 

bytes[7] = 0x40; // Load the hex string "40 72 50 82 00 00 00 00" 
bytes[6] = 0x72; 
bytes[5] = 0x50; 
bytes[4] = 0x82; 
bytes[3] = 0x00; 
bytes[2] = 0x00; 
bytes[1] = 0x00; 
bytes[0] = 0x00; 

my_double = doubles[0]; 

document.write(my_double); // 293.03173828125 

Dies geht von einer Little-Endian-Maschine.

Leider hat Chrome Float64Array nicht, obwohl es Float32Array hat. Das obige Beispiel funktioniert in Firefox 4.0.1.