2009-10-20 19 views
8

Supponiamo che abbia un numero esadecimale "4072508200000000" e voglio il numero in virgola mobile che rappresenta (293.03173828125000) nel formato doppio IEEE-754 per essere inserire una variabile JavaScript.Convertire una stringa con una rappresentazione esadecimale di un doppio IEEE-754 nella variabile numerica JavaScript

Posso pensare a un modo che utilizza un mascheramento e una chiamata a pow(), ma esiste una soluzione più semplice?

È necessaria una soluzione lato client.

Questo può aiutare. È un sito Web che consente di inserire una codifica esadecimale di un IEEE-754 e ottenere un'analisi di mantissa ed esponente.

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

perché le persone tendono sempre a chiedere "perché ?," ecco perché: Sto cercando di compilare un'implementazione esistente ma incompleta di Google Procol buffer (protobuf).

+0

Personalmente vorrei iniziare tagliando il valore esadecimale con una regex nei pezzi separati. Poi, li valuterei come interi, e alla fine proverei a trasformarli in un float. Sembra qualcosa che sarà complicato, perché dovrai farlo in modo tale che il runtime di Javascript non perda alcun bit lungo la strada. – Pointy

+0

Per la massima portabilità, è necessario considerare che i doppi IEEE-754 possono essere big-endian o little-endian. Se si conosce la convenzione utilizzata dall'input esadecimale, una soluzione lato client che utilizza pow() dovrebbe essere portatile. Se si decide di utilizzare una sorta di approccio di tipo puning, la piattaforma client endianness per i doppi dovrebbe essere controllata per prima. –

+0

@Jim Lewis: Ho una bandiera che mi dice big o little endian. – Nosredna

risposta

10

Non so di un buon modo. Certamente può essere fatto nel modo più difficile, ecco un esempio singola precisione totalmente all'interno di JavaScript:

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

Un'implementazione produzione dovrebbe prendere in considerazione che la maggior parte dei campi hanno valori magici, tipicamente implementato specificando un'interpretazione speciale per quello che sarebbe stato il più grande o il più piccolo. Quindi, rilevare NaN s e infiniti. L'esempio sopra dovrebbe controllare i negativi. (a & 0x80000000)

Aggiornamento: Ok, ce l'ho anche per il doppio. Non è possibile estendere direttamente la tecnica di cui sopra perché la rappresentazione JS interna è doppia e quindi, per definizione, può gestire al meglio una stringa di lunghezza 52 e non può spostarsi di oltre 32.

Ok, per fare il doppio, prima interrompi come una stringa le 8 cifre basse o 32 bit; elaborarli con un oggetto separato. Poi:

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> 

ho mantenuto l'esempio precedente perché è dal OP. Un caso più difficile è quando i 32 bit bassi hanno un valore. Qui è la conversione di 0x40725082deadbeef, una full-precisione doppia:

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> 

Ci sono alcune ovvie sottoespressioni è possibile fattore di fuori, ma ho lasciato in questo modo modo da poter vedere come si riferisce al formato.

+0

Il mio ultimo esempio è d'accordo con il tuo sito collegato per 0x40725082deadbeef. – DigitalRoss

+0

Molto bello. Grazie. – Nosredna

+0

Perché questo codice (secondo esempio) restituisce '1.1125369292536007e-308' quando a = 0? – etuardu

4

Una rapida aggiunta alla soluzione di DigitalRoss, per chi trova questa pagina tramite Google come ho fatto io.

A parte i casi limite di +/- Infinity e Nan, che mi piacerebbe ingresso, è inoltre necessario prendere in considerazione il segno del risultato:

s = a >> 31 ? -1 : 1 

è quindi possibile includere s nella moltiplicazione finale per ottenere il risultato corretto.

Penso che per una soluzione little-endian sarà necessario anche invertire i bit in a e b e scambiarli.

1

Il nuovo Typed Arrays meccanismo permette di fare questo (ed è probabilmente un meccanismo ideale per l'implementazione buffer di protocollo):

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 

Questo presuppone una macchina little-endian.

Sfortunatamente Chrome non ha Float64Array, sebbene abbia Float32Array. L'esempio sopra funziona in Firefox 4.0.1.

Problemi correlati