konwersja uint8 Data [4] -> float

0

Witam,
Parę minut temu szukałem rozwiązania konwersji tablice 4 elementowej uint 8 na float,
znalazłem to

float x = *(float *)&Data; 

rozwiązanie pochodzi ze strony http://www.microchip.com/forums/m590535.aspx .
Byłbym niezmiernie wdzięczy gdyby, ktoś mógłby wyjaśnić w kilku słowach co się tak naprawdę tutaj dzieje? Ta liczba wskaźników mnie przeraża.

&Data

Wskazuje na pierwszy element tablicy, nie wiem natomiast jak interpretować pozostały kod.

5

To jest prawie na pewno UB (nawet jeśli prawie wszędzie działa. Z naciskiem na prawie). Do czegoś takiego używaj memcpy lub std::copy w c++.

float x;
memcpy(&x, data, sizeof(x));
0

Dlaczego UB? Strict aliasing rule?

4

To raz, ale bardziej istotne jest, że standard ma "białą listę" możliwych dostępów do różnych typów poprzez glvalue do innych typów. Wybrany tutaj cast się na niej nie znajduje.

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
the dynamic type of the object,

  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.5) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.
0

Zaraz @kq będzie wrzeszczał że to ub, ale przyjęło się robić to przez unię, i wtedy działa.

union u
{
    float f;
    uint8_t b[4];
};

memcpy ma to do siebie, że kopiuje pamięć, co jest powolne.

3

Przez unię jest legalne tylko w C99/C11. W przeciwnym wypadku to UB i kompilator (obecny albo przyszły) może zrobić wszystko, np. kompletnie zignorować taki zapis.

A co do "powolnego kopiowania pamięci", powiedz mi proszę o której instrukcji mówisz: https://godbolt.org/g/GSJ44Q

3
Azarien napisał(a):

Zaraz @kq będzie wrzeszczał że to ub, ale przyjęło się robić to przez unię, i wtedy działa.

union u
{
    float f;
    uint8_t b[4];
};

memcpy ma to do siebie, że kopiuje pamięć, co jest powolne.

Type punning przez unię w C++ to UB, tylko w C tak można robić.
memcpy nie musi nic kopiować i należy go stosować w przypadku takich, niebezpiecznych rzutowań.

1
uint8_t Data[4] = { 0x00, 0x00, 0x80, 0x3F };
float x = *(float *)&Data[0]; // ewentualnie: x = *reinterpret_cast<float *>(&Data[0]);
printf("%f\n", x);

&Data[0] (lub po prostu Data) da ci uint8_t *, to sobie castujesz do float * i wyciągasz to co leży pod tym wskaźnikiem jako float.
Całość można by zapisać tak:

uint8_t Data[4] = { 0x00, 0x00, 0x80, 0x3F };
uint8_t *pd = &Data[0];
float *pvalue = (float *)(pd);
float value = *pvalue;

Trzeba jednak pamiętać, że to Undefined Behaviour jak to wspomnieli wyżej, ale raczej w znacznej większości przypadków to zadziała.

0

Tak przy okazji wrzucę. Ostatnio potrzebowałem zapisać coś na STM32 do pamięci flash.
Przykład pochodzi z katalogu

STM32Cube_FW_L4_V1.4.0\Projects\STM32L476RG-Nucleo\Examples\FLASH\FLASH_FastProgram\

Z paczki udostępnionej przez producenta procesorów
http://www.st.com/en/embedded-software/stm32cubel4.html

plik main.c

volatile uint64_t data64 = 0;
#define FLASH_USER_START_ADDR   ADDR_FLASH_PAGE_256
...
...
...
 Address = FLASH_USER_START_ADDR;
data64 = *(volatile uint64_t *)Address;

plik main.h
#define ADDR_FLASH_PAGE_256 ((uint32_t)0x08080000) /* Base @ of Page 256, 2 Kbytes */

Wydaje mi się, że nie jest to więc UB:)

2

Artykuł w temacie http://pzemtsov.github.io/2016/11/06/bug-story-alignment-on-x86.html

TL;DR to UB i może spowodować błędy z wyrównaniem pamięci.

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