Reprezentacja float jako liczba hex/uint ?

0

Jak zinterpretować zmienną float jako int-a ?
np. float o wartości 8.0 to uint 0x41000000

Bo kompilatorowi nie podoba się mój kod:

uint32_t float2uin32(float data)
{
   return *(uint32_t *)&data; // error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
}

https://godbolt.org/z/W83PM5Maa

8

Jedyny zgody ze standardem sposób to:

#include <cstdint>
#include <cstring>
#include <iostream>

uint32_t float2uin32(float data) {
    uint32_t ret = 0;
    std::memcpy(&ret, &data, sizeof(float));
    return ret;
}

int main() {
    float f{8};
    uint32_t f_uint = float2uin32(f);
    std::cout << "float=" << f << " uint32_t=" << f_uint << " hex:0x"
              << std::hex << f_uint;
}
0

A nie przejdzie *((uint32_t*)((void*)&data)))?

0

Pytanie czy chcesz odczytać mantyse itp. czy np. chcesz po prostu np. z float 5, int 5 dostać.

*&data

4
hauleth napisał(a):

Jedyny zgody ze standardem sposób to:

#include <cstdint>
#include <cstring>
#include <iostream>

uint32_t float2uin32(float data) {
    uint32_t ret = 0;
    std::memcpy(&ret, &data, sizeof(float));
    return ret;
}

int main() {
    float f{8};
    uint32_t f_uint = float2uin32(f);
    std::cout << "float=" << f << " uint32_t=" << f_uint << " hex:0x"
              << std::hex << f_uint;
}

Raczyłbym się nie zgodzić: std::hexfloat
https://godbolt.org/z/TrGW5fzaE

#include <iostream>
#include <iomanip>
#include <string_view>
#include <cmath>

using namespace std::literals;

std::string_view show_classification(double x) {
    switch(std::fpclassify(x)) {
        case FP_INFINITE:  return "Inf"sv;
        case FP_NAN:       return "NaN"sv;
        case FP_NORMAL:    return "normal"sv;
        case FP_SUBNORMAL: return "subnormal"sv;
        case FP_ZERO:      return "zero"sv;
        default:           return "unknown"sv;
    }
}

int main()
{
    double x;
    while (std::cin >> x) {
        std::cout 
            << std::defaultfloat << std::setw(10) << std::right << x 
            << "    " 
            << std::hexfloat << std::left << std::setw(24) << x
            << show_classification(x) 
            << '\n';
    }

    return 0;
}

Rozwiązanie od hauleth z memcpy jedynie ukrywa fakt, że standard C++ nie definiuje jaka jest reprezentacja liczb zmiennoprzecinkowych, ergo ten sam kod może dać zupełnie inny wynik na różnych platformach.

4

Spotkałem się z tym problemem w praktyce i chociaż odpowiedzi @hauleth dałem plusa, to trzeba mieć na uwadze to, na co zwrócił uwagę @MarekR22

ergo ten sam kod może dać zupełnie inny wynik na różnych platformach

By mieć pewność, że rozwiązanie nie ugryzie mnie w przyszłości w tyłek problem rozwiązałem poprzez liczenie numeratora i denominatora. Chociaż jest nieco wolniejsze niż memcpy to jednak posiadanie reprezentacji, która matematycznie odwróci Ci z powrotem tego samego (albo prawie tego samego w zależności jakie podejście wybierzesz) float'a niezależnie od implementacji/reprezentacji liczb zmiennoprzecinkowych w procesorze daje większy komfort. Szczególnie że w sytuacji w której poszukuje się takich rozwiązań to najczęściej na potrzeby jakiejś serializacji, która już sama w sobie dostarcza problemów związanych ze sprzętem jak endianness naprzykład.

3
Manna5 napisał(a):

A nie przejdzie *((uint32_t*)((void*)&data)))?

Nie można dereferować wskaźnika na int (et consortes), jeśli ten wskaźnik był rzutowany ze wskaźnika na floata. Ani odwrotnie. I to nawet niezależnie od tego ile pośrednich etapów zrobisz, z void* pomiędzy.

Ale dozwolonym sposobem jest rzutować float* na unsigned char* i tak czytać pojedynczo bajty.

2
MarekR22 napisał(a):

Rozwiązanie od hauleth z memcpy jedynie ukrywa fakt, że standard C++ nie definiuje jaka jest reprezentacja liczb zmiennoprzecinkowych, ergo ten sam kod może dać zupełnie inny wynik na różnych platformach.

Nie, mi chodzi o to, że to jest jedyny poprawny sposób, bo to jest jedyny zgodny ze standardem sposób na type punning. Wszystko inne to UB.

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