2012-12-20 18 views
13

Uwaga: błędnie zapytałem o oryginalną wersję: static_cast; właśnie dlatego pierwsza z najlepszych odpowiedzi wymienia najpierw static_cast.Czy można bezpiecznie reinterpretować wartość liczby całkowitej na wartość float?

Mam kilka plików binarnych z małymi wartościami zmiennoprzecinkowymi. Chcę je czytać w sposób niezależny od maszyny. Moje procedury zamiany bajtów (z SDL) działają na liczbach całkowitych bez znaku.

Czy można bezpiecznie rzucać między intami a spławikami?

float read_float() { 
    // Read in 4 bytes. 
    Uint32 val; 
    fread(&val, 4, 1, fp); 
    // Swap the bytes to little-endian if necessary. 
    val = SDL_SwapLE32(val); 
    // Return as a float 
    return reinterpret_cast<float &>(val); //XXX Is this safe? 
} 

Chcę, aby to oprogramowanie było jak najbardziej przenośne.

+2

Możesz "reinterpretować" tylko wskaźniki lub typy odniesienia; Twój kod nie zostanie skompilowany. – Praetorian

+1

Prawdopodobnie masz na myśli coś w rodzaju wyniku float = reintrepret_cast (val); –

+0

Ups. Naprawdę powinienem był najpierw spróbować skompilować:/ – QuasarDonkey

Odpowiedz

24

Cóż, static_cast jest "bezpieczny" i ma zdefiniowane zachowanie, ale prawdopodobnie nie jest to potrzebne. Konwersja wartości całki do typu zmiennoprzecinkowego po prostu będzie próbowała reprezentować tę samą wartość integralną w docelowym typie zmiennoprzecinkowym. To znaczy. 5 typu int zmieni się w 5.0 typu float (zakładając, że jest on dokładnie reprezentowany).

To, co robisz, to budowanie reprezentacji obiektu o wartości float w pamięci zadeklarowanej jako zmienna Uint32. Aby wygenerować wynikową wartość float, musisz ponownie zinterpretować tę pamięć za pomocą . Miałoby to być osiągnięte przez reinterpret_cast

assert(sizeof(float) == sizeof val); 
return reinterpret_cast<float &>(val); 

albo, jeśli wolisz, wersję wskaźnika z tego samego

assert(sizeof(float) == sizeof val); 
return *reinterpret_cast<float *>(&val); 

Chociaż ten rodzaj typu paronomazja nie jest gwarantowane do pracy w kompilator, który następuje semantyka stricte-aliasingowa. Innym podejściem byłoby zrobienie tego w celu zaimplementowania reinterpretacji pamięci. Niektóre kompilatory ścisłych-aliasing semantyka zastrzegają podejścia Unii lokalnych jako oficjalnie obsługiwanej metody typu-paronomazja

assert(sizeof(float) == sizeof(Uint32)); 

union { 
    Uint32 val; 
    float f; 
} u = { val }; 

return u.f; 
+0

Przepraszam, jesteś poprawny (miałem na myśli reinterpret_cast, nie static_cast). Zaktualizowałem pytanie, aby to odzwierciedlić. – QuasarDonkey

+0

Dzięki. Nie zdawałem sobie sprawy z terminu "typowanie". Okazało się kilka przydatnych informacji. W oparciu o to, co przeczytałem, myślę, że pójdę z sztuczką * union *, wydaje się być dobrze obsługiwana. – QuasarDonkey

+0

Co się stanie, jeśli najpierw wyrzucimy, aby unieważnić *, a potem wypłyniemy *, aby uzyskać pomnożenie 4? Czy to jest bezpieczne? –

2

W skrócie, jest to błędne. Przerzucasz liczbę całkowitą do wartości zmiennoprzecinkowej, która zostanie zinterpretowana przez kompilator jako liczba całkowita w danym momencie. Przedstawione powyżej rozwiązanie związków zawodowych.

Innym sposobem, aby zrobić to samo coś takiego jak Unia ma byłoby użyć to:

return *reinterpret_cast<float*>(&val); 

Jest równie bezpieczne/niebezpieczne jako rozwiązanie unii powyżej, i zdecydowanie polecam dochodzić upewnij się, że float ma ten sam rozmiar co int.

Chciałbym również ostrzec, że istnieją AREpformaty o formacie zmiennoprzecinkowym, które nie są zgodne z IEEE-754 lub IEEE-854 (te dwa standardy mają ten sam format dla liczb zmiennoprzecinkowych, nie jestem do końca pewien, czym jest różnica szczegółów, aby bądź szczery). Tak więc, jeśli masz komputer, który używa innego formatu zmiennoprzecinkowego, to się przewróci. Nie jestem pewien, czy jest jakikolwiek sposób, aby to sprawdzić, pomijając być może posiadanie przechowywanego w zbiorze bajta gdzieś przechowywanego, wraz z oczekiwanymi wartościami w float, a następnie przekonwertuj wartości i zobacz, czy pojawi się "prawy".

+0

Cóż, teraz czytałem artykuł wiki o typowaniu. Mówi on: "Na wielu typowych platformach użycie wskaźnika pingowania może powodować problemy, jeśli różne wskaźniki są wyrównane w sposób specyficzny dla maszyny ... Ten problem z aliasingiem może zostać naprawiony przez użycie unii". – QuasarDonkey

+0

Jeśli typy danych różnią się wyrównaniem, możesz mieć problemy z ich utrzymaniem w związku, ponieważ nie ma gwarancji, że dane odczytywane z liczby całkowitej pokrywają się z wartością zmiennoprzecinkową. Ale myślę, że moglibyśmy mieć kompilator, który myśli, że można umieścić liczbę całkowitą pod adresem X z wyrównaniem 2, a następnie mieć spacje wyrównane do 4 bajtów, w takim przypadku spowodowałoby to awarię lub zachowanie się źle) NAJLEPSZY (najbardziej przenośne) jest prawdopodobnie do przechowywania danych zmiennoprzecinkowych jako tekst lub jako punkt stały w formacie całkowitym. W ten sposób nie ma wątpliwości, co to znaczy, ani o wyrównaniu. –

+0

To ma sens, ale myślę, że pozostanę przy unii, ponieważ wydaje się ona najlepszą opcją dla plików binarnych. Niestety, nie mogę używać tekstu, ponieważ obsługuję pliki z dotychczasowego systemu. – QuasarDonkey

Powiązane problemy