konwersja uint8 Data [4] -> float

Odpowiedz Nowy wątek
Pijany Pomidor
2016-08-13 10:16
Pijany Pomidor
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.

Pozostało 580 znaków

kq
2016-08-13 10:18
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 4 godziny temu

Lokalizacja: Szczecin

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));

edytowany 2x, ostatnio: kq, 2016-08-13 10:19

Pozostało 580 znaków

Wielki Terrorysta
2016-08-13 11:35
Wielki Terrorysta
0

Dlaczego UB? Strict aliasing rule?

Pozostało 580 znaków

kq
2016-08-13 11:42
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 4 godziny temu

Lokalizacja: Szczecin

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.

edytowany 1x, ostatnio: kq, 2016-08-13 11:44
a char or unsigned char type a uint8_t to char. - Azarien 2016-08-13 13:34
Można float oglądać jako char. Odwrotna konwersja nie jest tutaj zezwolona. - kq 2016-08-13 14:05

Pozostało 580 znaków

2016-08-13 13:33

Rejestracja: 16 lat temu

Ostatnio: 14 godzin temu

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.

Pozostało 580 znaków

kq
2016-08-13 14:05
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 4 godziny temu

Lokalizacja: Szczecin

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


Pokaż pozostałe 5 komentarzy
Spoko. Przy okazji: nawet jeśli "główna" funkcja musi być wygenerowana dla dowolnego alignmentu, nic nie stoi na przeszkodzie, aby kompilator wyprodukował efektywny kod jeśli alignment jest bezpieczny: https://godbolt.org/g/jD2Ehs - kq 2016-08-16 15:07
Bezpieczny i w miarę efektywny kod bez magii masz w funkcji bam w moim przykładzie. Jedyny efektywny kod to ten z pierwszej funkcji, ale wtedy programista musi pilnować wyrównania argumentu sam. - kapojot 2016-08-16 15:11
Bezpieczny i efektywny jest w wersji z unią - ale wyłącznie w C99/11. W pierwszym wypadku masz UB, niezależnie jak na to patrzeć. - kq 2016-08-16 15:12
Efektywny kod na ARM-ie jest tylko wtedy, gdy korzysta z wyrównanych danych. Jak zaczniesz ładować niewyrównane dane, to będzie wydajnościowy sajgon. O C++ mam minimalne pojęcie, więc nie będę się wypowiadał nt. UB w przypadku z unią. - kapojot 2016-08-16 15:36
Nie zawsze masz wybór (np. deserializacja danych z pliku/socketa). - kq 2016-08-16 15:38

Pozostało 580 znaków

2016-08-13 14:07

Rejestracja: 8 lat temu

Ostatnio: 2 miesiące temu

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ń.

wątpię żeby jakikolwiek kompilator robił specjalnie wyjątek tak żeby działało w C a nie działało w C++. - Azarien 2016-11-07 21:25

Pozostało 580 znaków

2016-08-13 15:18

Rejestracja: 7 lat temu

Ostatnio: 3 dni temu

Lokalizacja: Wrocław

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.


Asm/C/C++
edytowany 1x, ostatnio: mwl4, 2016-08-13 15:20
Pokaż pozostałe 4 komentarze
memcpy jest poprawną metodą. Co do wolniejsza - odpowiedz w takim razie na pytanie zadane w tym poście: http://4programmers.net/Forum/1278272 - kq 2016-08-13 15:31
@kq bez optymalizacji troszkę więcej instrukcji: https://godbolt.org/g/ZERmZI - mwl4 2016-08-13 15:41
Tylko kto normalny benchmarkuje bez optymalizacji? - kq 2016-08-13 15:42
No ale jednak, w pewnych warunkach użycie memcpy wygeneruje więcej instrukcji. Poza tym, nie każdy kompilator pewnie umiałby to zoptymalizować odpowiednio. - mwl4 2016-08-13 15:46
W warunkach gdzie nie ma to znaczenia (czyli developerka bez optymalizacji) - nie ma to znaczenia. Jeśli bawimy się w egzotyczne kompilatory - spoko, ale w pytaniu nie było nic takiego, a nawet jeśli to powinno się wychodzić od poprawnego kodu i dopiero potem optymalizować pod konkretną platformę. - kq 2016-08-13 15:50

Pozostało 580 znaków

Pijany Pomidor
2016-11-07 13:20
Pijany Pomidor
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:)

Pozostało 580 znaków

2016-11-07 13:38
Moderator

Rejestracja: 12 lat temu

Ostatnio: 12 minut temu

2

Artykuł w temacie http://pzemtsov.github.io/201[...]g-story-alignment-on-x86.html

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


Pozostało 580 znaków

Odpowiedz

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