2009-10-20 13 views
8

Załóżmy, że mam numer szesnastkowy "4072508200000000" i chcę, aby liczba zmiennoprzecinkowa, którą reprezentuje (293.03173828125000) w podwójnym formacie IEEE-754, była wstawić do zmiennej JavaScript.Konwertuj ciąg znaków z reprezentacją heksadecymalną na IEEE-754 w zmiennej numerycznej JavaScript

Mogę wymyślić sposób, który używa trochę maskowania i wywołanie pow(), ale czy istnieje prostsze rozwiązanie?

Potrzebne jest rozwiązanie po stronie klienta.

To może pomóc. Jest to strona internetowa, która pozwala wprowadzić kodowanie heksadecymalne IEEE-754 i uzyskać analizę mantysy i wykładnika.

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

Ponieważ ludzie zawsze wydają się zapytać „dlaczego ?,” oto dlaczego: Staram się wypełnić istniejącą ale niepełną realizację Procol buforów Google (protobuf).

+0

Osobiście zacznę od krojenia wartości szesnastkowej za pomocą wyrażenia regularnego w osobnych częściach. Następnie oceniłbym je jako liczby całkowite, a na koniec spróbowałbym zamienić je w zmiennoprzecinkowe. Wygląda to na coś, co będzie trudne, ponieważ będziesz musiał to zrobić tak, że środowisko wykonawcze JavaScript nie straci żadnych bitów po drodze. – Pointy

+0

Dla maksymalnej mobilności, powinieneś wziąć pod uwagę, że debel IEEE-754 może być zarówno big-endian, jak i little-endian. Jeśli wiesz, która konwencja jest używana przez wejście szesnastkowe, rozwiązanie po stronie klienta korzystające z pow() powinno być przenośne. Jeśli zdecydujesz się na zastosowanie pewnego rodzaju metody typowania, typowanie platformy klienta dla debla powinno być najpierw sprawdzone. –

+0

@ Jim Lewis: Mam flagę, która mówi mi o dużym lub małym endianie. – Nosredna

Odpowiedz

10

Nie znam dobrej drogi. To na pewno można zrobić w przykry sposób, oto przykład pojedynczej precyzji całkowicie w ciągu JavaScript:

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

Implementacja produkcja powinna uznać, że większość pól mają wartości magiczne, zazwyczaj realizowane poprzez określenie specjalnej interpretacji przez co byłby największy lub najmniejszy. Więc wykryjmy nieskończoności. Powyższy przykład powinien sprawdzać negatywy. (& 0x80000000)

Aktualizacja: Ok, mam też dla podwójnych. Nie można bezpośrednio rozszerzyć powyższej techniki, ponieważ wewnętrzna reprezentacja JS jest podwójna, a więc z definicji może obsłużyć w najlepszym przypadku łańcuch o długości 52 i nie może w ogóle przejść o więcej niż 32.

OK, aby wykonać dwukrotnie najpierw odetnij jako ciąg niskie 8 cyfr lub 32 bity; przetwarzać je za pomocą oddzielnego obiektu. Następnie:

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> 

Zachowałem powyższy przykład, ponieważ pochodzi z PO. Trudniej jest, gdy niskie 32 bity mają wartość. Oto konwersja 0x40725082deadbeef, pełne precyzji 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> 

Istnieje kilka oczywistych Podwyrażenia można się czynnikiem ale zostawiłem to w ten sposób, dzięki czemu można zobaczyć, jak to odnosi się do wzoru.

+0

Mój ostatni przykład nie zgadza się z połączoną stroną dla 0x40725082deadbeef. – DigitalRoss

+0

Bardzo ładne. Dzięki. – Nosredna

+0

Dlaczego ten kod (drugi przykład) zwraca '1.1125369292536007e-308', gdy a = 0? – etuardu

4

Szybki dodatek do rozwiązania DigitalRoss dla osób, które znalazły tę stronę za pośrednictwem Google tak jak ja.

Oprócz przypadków brzegowych dla +/- nieskończoności i NaN, które chciałbym na wejście, trzeba także wziąć pod uwagę znak Rezultat:

s = a >> 31 ? -1 : 1 

można następnie dołączyć s w ostatecznym mnożeniu, aby uzyskać poprawny wynik.

Myślę, że dla rozwiązania małego endianina trzeba będzie również odwrócić bity w a i b i zamienić je.

1

Nowy Typed Arrays mechanizm pozwala to zrobić (i prawdopodobnie jest to idealne urządzenie dla realizacji bufory Protocol):

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 

Zakłada się trochę-endian maszyny.

Niestety Chrome nie ma Float64Array, mimo że ma Float32Array. Powyższy przykład działa w Firefoksie 4.0.1.