Jak dostać się do bitów zmiennej typu float?

0

Mam kod:

    float n = 123.2;
    n |= 1<<0; // error: C2296: '|=': illegal, left operand has type 'float'

Chciałbym sobie ustawić/odczytać któryś bit w zmiennej float.
Rozumiem, że operatory |= oraz &= nie są zdefiniowane dla zmiennej typu float (jeśli się mylę proszę mnie poprawić).
Jak się dostać do tego bitu?

Z int, lub char nie ma tego problemu.

0

Najbezpieczniej kopiując fragment pamięci,,, mozesz tez odpowiednio rzutować. Ale wiesz co zmieni ustawienie któregoś bitu?

0

Wiem co zmieni.
A jak skopiować fragment pamięci? np jeśli mam wskaźnik na float, jak skopiować tego floata bez żadnej konwersji do zmiennej typu int(tak, żeby był kopiowany bit w bit).
Zakładam, że floati intmają taki sam rozmiar.

Generalnie dążę do tego żeby zobaczyć jak komputer zapisał daną liczbę w pamięci.

0
static_assert(sizeof(float) == sizeof(int), "sizeof(float) != sizeof(int)");
float value = 1.f;
int value_int = 0;
memcpy(&value_int, &value, sizeof(int));
std::cout << std::hex << value_int;
1

Możesz pobrać wskaźnik float*, zrzutować na unsigned char* i oglądać bajty.

Ale uważaj, rzutowanie tak otrzymanego char* na int* żeby „zrobić sobie inta” i próba odczytania tego inta jest UB (undefined behaviour).
Podobnie niedozwolone jest rzutowanie bezpośrednio float* na int* i czytanie inta.

0
Azarien napisał(a):

Możesz pobrać wskaźnik float*, zrzutować na unsigned char* i oglądać bajty.

Ale uważaj, rzutowanie tak otrzymanego char* na int* żeby „zrobić sobie inta” i próba odczytania tego inta jest UB (undefined behaviour).
Podobnie niedozwolone jest rzutowanie bezpośrednio float* na int* i czytanie inta.

Niby co w tym ma być niedozwolone?

Format float wygląda tak:
znak cecha 2^mantysa
gdzie:

  1. znak liczby to najstarszy bit, czyli 31: 1 znaczy minus, a zero to +.
  2. kolejne 7 bitów, czyli 30 do 26, to cecha, z biasem 127
  3. mantysa to reszta bitów, czyli: 32 - 1 - 7 = 24 bity.

zadanie:

float x = 1.5;

i teraz robimy tak:
int &y = (int)&x;

jaką wartość ma y:
y = ?

1
float foo(float in)
{
    union {float f; unsigned u} tmp;

    tmp.f = in;
    tmp.u &= 0x7FFFFFFF; //przykładowe zastosowanie
    return tmp.f;
}

float bar(float in)
{
    unsigned u;

    std::memcpy(&u, &in, sizeof(float)); //kopiowanie bitów z in do u
    u &= 0x7FFFFFFF; //przykładowe zastosowanie
    std::memcpy(&in, &u, sizeof(float)); //kolejne kopiowanie z u do in

   return in;
}

Funkcja bar nie łamie strict aliasign rule, a funkcja foo oficjalnie nie jest "legalna" w c++, ale w praktyce wszystkie kompilatory ją wspierają. Poza bar, w c++ cała reszta (wskaźniki + unie) nie ma gwarancji że będzie działać na dowolnym kompilatorze zgodnym ze standardem.

0

A po co tam kasujesz znak?

union {float f; unsigned u} tmp;

tmp.f = 1.67;

return tmp.u; // to jest wersja binarna tego floata

2

zadanie:

float x = 1.5;

i teraz robimy tak:

int &y = *(int*)&x;

jaką wartość ma y:

y = ?

Tu nie chodzi o wartość, to jest UB – konkretnie ta gwiazdka tuż po znaku równości.

Kompilator może - zgodnie ze standardem - założyć, że zmiana wartości x na pewno nie zmienia wartości y. Twój kod tę zasadę (zwaną strict aliasing) łamie.

0

Wybacz, ale jakiekolwiek czytanie danych nie może łamać jakichkolwiek zasad programowania.

Ostatecznie wiadomo że pamięć w komputerze to ciągi bitów, i... więcej tam nie ma.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0