2009-10-20 6 views
8

Supposons que je possède un nombre hexadécimal "4072508200000000" et que le numéro à virgule flottante qu'il représente (293.03173828125000) en format double IEEE-754 soit mettre dans une variable JavaScript.Convertir une chaîne avec une représentation hexadécimale d'un double IEEE-754 en variable numérique JavaScript

Je peux penser à un moyen qui utilise un masquage et un appel à pow(), mais existe-t-il une solution plus simple?

Une solution côté client est nécessaire.

Cela peut aider. C'est un site Web qui vous permet d'entrer un codage hexadécimal d'un IEEE-754 et d'obtenir une analyse de la mantisse et de l'exposant.

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

Parce que les gens ont toujours tendance à demander « pourquoi ?, » voici pourquoi: Je suis en train de remplir une implémentation existante mais incomplète de Procol de Google (buffers protobuf).

+0

Personnellement, je commencerais par hacher la valeur hexadécimale avec une regex dans les pièces séparées. Ensuite, je les évaluerais comme des entiers, et finalement j'essaierais de les transformer en flotteur. Cela semble être quelque chose qui va être difficile, car vous allez devoir le faire de telle sorte que le runtime Javascript ne perde aucun bit en cours de route. – Pointy

+0

Pour une portabilité maximale, vous devez considérer que les doubles IEEE-754 peuvent être big-endian ou little-endian. Si vous savez quelle convention est utilisée par l'entrée hexadécimale, une solution côté client utilisant pow() doit être portable. Si vous décidez d'utiliser une approche de type punition, vous devrez d'abord vérifier l'endianness de la plate-forme client pour les doubles. –

+0

@Jim Lewis: J'ai un drapeau qui me dit gros ou petit boutiste. – Nosredna

Répondre

10

Je ne connais pas un bon moyen. Il peut certainement être fait à la dure, voici un exemple simple précision tout à fait dans JavaScript:

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

Une mise en œuvre de la production devrait considérer que la plupart des champs ont des valeurs magiques, généralement mis en œuvre en spécifiant une interprétation particulière pour ce aurait été le plus grand ou le plus petit. Donc, détecter NaN s et infinis. L'exemple ci-dessus devrait vérifier les négatifs. (& 0x80000000)

Mise à jour: Ok, je l'ai aussi pour les doublons. Vous ne pouvez pas étendre directement la technique ci-dessus parce que la représentation JS interne est un double, et donc par sa définition, elle peut gérer au mieux une chaîne de bits de longueur 52, et elle ne peut pas changer de plus de 32.

Ok, pour faire doublez vous coupez d'abord comme une chaîne les 8 chiffres bas ou 32 bits; traitez-les avec un objet séparé. Puis:

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> 

J'ai gardé l'exemple ci-dessus parce qu'il provient de l'OP. Un cas plus difficile est lorsque les 32 bits faibles ont une valeur. Voici la conversion de 0x40725082deadbeef, une double précision absolue:

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> 

Il y a quelques sous-expressions évidentes que vous pouvez factoriser mais je l'ai laissé cette façon afin que vous puissiez voir comment il se rapporte au format.

+0

Mon dernier exemple est d'accord avec votre site lié pour 0x40725082deadbeef. – DigitalRoss

+0

Très bien. Merci. – Nosredna

+0

Pourquoi ce code (second exemple) renvoie-t-il '1.1125369292536007e-308' quand a = 0? – etuardu

4

Un ajout rapide à la solution de DigitalRoss, pour ceux qui trouvent cette page via Google comme je l'ai fait.

En dehors des cas de pointe pour +/- Infinity et NaN, que j'aimerais entrée, vous devez également tenir compte du signe du résultat:

s = a >> 31 ? -1 : 1 

Vous pouvez ensuite inclure s dans la multiplication finale pour obtenir le bon résultat.

Je pense que pour une solution little-endian, vous devrez également inverser les bits dans a et b et les échanger.

1

Le nouveau mécanisme Typed Arrays vous permet de le faire (et est probablement un mécanisme idéal pour la mise en œuvre des tampons de protocole):

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 

Cela suppose une machine peu endian.

Malheureusement, Chrome n'a pas Float64Array, bien qu'il ait Float32Array. L'exemple ci-dessus fonctionne dans Firefox 4.0.1.

Questions connexes