Úprava #define makra dělící 32bit val na 16bit values

C,C++,C#

Moderátori: psichac, Moderátori

Používateľov profilový obrázok
jirka.jirka.
Ultimate člen
Ultimate člen
Príspevky: 1537
Dátum registrácie: 17 Okt 2008, 00:00
Bydlisko: Uherské Hradiště
Kontaktovať používateľa:

Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa jirka.jirka. » 14 Apr 2021, 10:18

Zdravím přátelé,

převzal jsem projekt, který funguje, ale je zde jedna věc se kterou nejsem spokojený, ale nevidím teď jednoduchou cestu, jak to elegantně vyřešit, protože tohle #define je napříč celým projektem hodně používané, tak přemýšlím, jak jej upravit, aby kompilátoru to přestalo vadit.

Kompilátor totiž takhle umí pracovat pouze s bytes (uint8_t). Už ale nevím, kde jsem tuto informaci našel. Jak to vidíte vy? Má tohle nějaké elegantní řešení? Díky zy info

Kód: Vybrať všetko

// Konverze uint32_t na MSB a LSB uint16_t
#define H_INT(input) (((uint16_t *) &input)[1])
#define L_INT(input) (((uint16_t *) &input)[0])
0

stefanSK
Pokročilý člen
Pokročilý člen
Príspevky: 752
Dátum registrácie: 24 Jún 2010, 00:00
Bydlisko: Trnava
Vek: 68

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa stefanSK » 14 Apr 2021, 10:29

Čo presne kompilátor hlási?

Možno by stačilo to prepísať na:

// Konverze uint32_t na MSB a LSB uint16_t
#define H_INT(input) *((uint16_t *) input)+1)
#define L_INT(input) *((uint16_t *) input)
0
S.K.

StavJi
Okoloidúci
Okoloidúci
Príspevky: 42
Dátum registrácie: 22 Jan 2014, 14:01
Bydlisko: Olomouc

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa StavJi » 14 Apr 2021, 11:48

co toto?

#define H_INT(input) (uint16_t)((input & 0xFFFF0000) >> 16)
#define L_INT(input) (uint16_t)(input & 0x0000FFFF)
0

Používateľov profilový obrázok
jirka.jirka.
Ultimate člen
Ultimate člen
Príspevky: 1537
Dátum registrácie: 17 Okt 2008, 00:00
Bydlisko: Uherské Hradiště
Kontaktovať používateľa:

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa jirka.jirka. » 14 Apr 2021, 11:55

No já mám takové tušení, že to asi úplně vypreparuju a skrze nějakou funkci si to budu ošetřovat sám. Makra jsou dobrá věc, ale zde nevím nevím... :pain:
0

Používateľov profilový obrázok
mac26
Zaslúžilý člen
Zaslúžilý člen
Príspevky: 1298
Dátum registrácie: 01 Feb 2010, 00:00
Bydlisko: Martin

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa mac26 » 14 Apr 2021, 12:49

preco si to komplikovat?

Kód: Vybrať všetko

#define H_INT(_val_) ((uint16_t) _val_)
#define L_INT(_val_) ((uint16_t) (_val_ >> 16))
Rozumnejsi kompilator by to mohol pochopit a prelozit aj bez toho posuvania
1
Obrázok 1

Používateľov profilový obrázok
budvar10
Pokročilý člen
Pokročilý člen
Príspevky: 983
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa budvar10 » 14 Apr 2021, 13:16

Elegantné riešenie je použiť union.
0

Používateľov profilový obrázok
Radus
Zaslúžilý člen
Zaslúžilý člen
Príspevky: 1497
Dátum registrácie: 27 Okt 2009, 00:00
Bydlisko: Prešov
Vek: 42

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa Radus » 14 Apr 2021, 14:31

...načo si to komplikovať? No, teroreticky len posunom pointera sa dokážeš dostať k MSB alebo LSB, bez nejakej inej operácii. Avšak, všetko záleží na organizácii pamati. Ak sú 8 bitové bloky, tak potom ak uložíš 16bit číslo, ľahko sa posunom pointra dostaneš k LSB a MSB.
Aby fungoval tvoj prípad, musela by byť organizácia pameti 16bitová a potom posun spraví presne to čo chceš pre 32bitové číslo. Avšak je to dosť na dlhé lakte. Tiež záleži či je to Big/Little Endian... kde by si to dostal práve naopak.
Asi nejlepšie bude tak ako si to písal, vypreparovať do funkcie a tú ešte označ ako inline...a bude. Alebo v #define posun ako písal mac.
0

Používateľov profilový obrázok
budvar10
Pokročilý člen
Pokročilý člen
Príspevky: 983
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa budvar10 » 14 Apr 2021, 15:41

Takto nejako:

Kód: Vybrať všetko

typedef union {
	uint32_t  value;
	struct { uint16_t l; uint16_t h; } ui;
} LH_INT;
Použiť to hádam vieš. Compiler sa nebude sťažovať, a fungovať to bude na ktoromkoľvek systéme s min. inštrukcií.
0

Používateľov profilový obrázok
jirka.jirka.
Ultimate člen
Ultimate člen
Príspevky: 1537
Dátum registrácie: 17 Okt 2008, 00:00
Bydlisko: Uherské Hradiště
Kontaktovať používateľa:

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa jirka.jirka. » 14 Apr 2021, 19:40

Radus napísal:
14 Apr 2021, 14:31
...načo si to komplikovať? No, teroreticky len posunom pointera sa dokážeš dostať k MSB alebo LSB, bez nejakej inej operácii. Avšak, všetko záleží na organizácii pamati. Ak sú 8 bitové bloky, tak potom ak uložíš 16bit číslo, ľahko sa posunom pointra dostaneš k LSB a MSB.
Aby fungoval tvoj prípad, musela by byť organizácia pameti 16bitová a potom posun spraví presne to čo chceš pre 32bitové číslo. Avšak je to dosť na dlhé lakte. Tiež záleži či je to Big/Little Endian... kde by si to dostal práve naopak.
Asi nejlepšie bude tak ako si to písal, vypreparovať do funkcie a tú ešte označ ako inline...a bude. Alebo v #define posun ako písal mac.
To je vončo. Funkce a tu prdnout jako inline a problém solve i do budoucna. :thumbup:
0

peterple
Ultimate člen
Ultimate člen
Príspevky: 2328
Dátum registrácie: 25 Jún 2013, 21:06
Bydlisko: Krajné
Vek: 57
Kontaktovať používateľa:

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa peterple » 14 Apr 2021, 21:31

Ja pre zmenu hlasujem za riešenie ktoré dal Mac. Union aj pole aj ukazatel je platformovo závislé. Ako bolo spomínané záleží na tom či je architektúra Little alebo Big Endian. Vrátane toho že si tiež myslím že to compiler vyoptimalizuje a žiadne rotácie tam nedá, len použije správny presun ako mu sa u to tam snažíte nadirigovať s tým unionom alebo poľom. Takto je to taká nášlapná mína na ktorú môže niekto stúpiť až po rokoch.
0
Ukáž múdremu chybu a on sa ti poďakuje. Ukáž chybu hlupákovi a on sa urazí.

Používateľov profilový obrázok
budvar10
Pokročilý člen
Pokročilý člen
Príspevky: 983
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa budvar10 » 15 Apr 2021, 07:06

Union je platformovo závislé? Nejaký príklad?
Ako bolo spomínané záleží na tom či je architektúra Little alebo Big Endian.
Ako to rieši to, čo dal mac26?

Nevravím, že riešenie od mac26 je zlé. Bežne sa to tak robí, ale niektoré kompilátory to preložia menej efektívne ako union, pretože to je mat. operácia.
0

Používateľov profilový obrázok
Radus
Zaslúžilý člen
Zaslúžilý člen
Príspevky: 1497
Dátum registrácie: 27 Okt 2009, 00:00
Bydlisko: Prešov
Vek: 42

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa Radus » 15 Apr 2021, 09:11

...lebo ani pomocou unionu si nemôžeš byť istý že ti vráti MSB/LSB kvoli Endianu. Riešiť to posunom je proste istota, aj keď je to matematická operácia. Je to o kompromise - výkonnosť, verzus 100% funkčnosť...
//Na procesoroch od Motoroly by si to mal naopak...a divná chyba na svete :)
0

stefanSK
Pokročilý člen
Pokročilý člen
Príspevky: 752
Dátum registrácie: 24 Jún 2010, 00:00
Bydlisko: Trnava
Vek: 68

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa stefanSK » 15 Apr 2021, 10:51

Stále si nenapísal, aké varovanie ti píše prekladač.
Existuje jeden súbor ieeefp.h, v ktorom sa definujú okrem iných vecí aj informácia o tom, či procesor
je big-endian - __IEEE_BIG_ENDIAN, alebo low-endian - __IEEE_LITTLE_ENDIAN.
Podľa týchto definícií si môžeš zadefinovať správne makrá pre operácie s bytami premennných.

Podľa mňa pre výsledný kód sú vhodné makrá čo si uviedol ako prvé, poprípade ekvivalent, čo som dal ja.
Tieto makrá platia pre __IEEE_LITTLE_ENDIAN.
Pre __IEEE_BIG_ENDIAN je to nopak.

Takže do programu vlož:
#include "ieeefp.h"

#if defined __IEEE_LITTLE_ENDIAN
.. vlastné definície H_INT, L_INT
#else
#if defined __IEEE_BIG_ENDIAN
.. vlastné definície H_INT, L_INT
#else
#error "neznámy typ procesora "
#endif
#endif
0
S.K.

JohnnyElektro
Stály člen
Stály člen
Príspevky: 386
Dátum registrácie: 16 Júl 2019, 14:22

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa JohnnyElektro » 15 Apr 2021, 12:34

V interpretovatelnom zdrojovom kode

Kód: Vybrať všetko

#define UInt16 short // ak nie je
inline Uint16 DostanWordNaPamati(bool high,(char*)neznamapamatnaneznamejplatforme)
(char*) LH=(char*)neznamapamatnaneznamejplatforme; // len tak pre istotu deklaracie
(UInt16*) LI=(UInt16*)LH;
(UInt16*) HI=(UInt16*)(LH+2);
if (high==true)
 return *HI 
else
return *LI;
0

peterple
Ultimate člen
Ultimate člen
Príspevky: 2328
Dátum registrácie: 25 Jún 2013, 21:06
Bydlisko: Krajné
Vek: 57
Kontaktovať používateľa:

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa peterple » 15 Apr 2021, 23:27

budvar10 napísal:
15 Apr 2021, 07:06
Union je platformovo závislé? Nejaký príklad?
Ako bolo spomínané záleží na tom či je architektúra Little alebo Big Endian.
Ako to rieši to, čo dal mac26?

Nevravím, že riešenie od mac26 je zlé. Bežne sa to tak robí, ale niektoré kompilátory to preložia menej efektívne ako union, pretože to je mat. operácia.
Príklad neviem. Nikdy som Big endian nejazdil, ale vychádza mi že poriadie zložiek unionu bude opačné ako pri Little Endian.

Inak celá táto akademická debata je zrejme o ničom. Dal som riešenie od Mac do AVR studia
image.png
A výsledok je že tam nie je žiadna rotácia. Trochu ma prekvapuje ako rieši to L_INT kedže je to iba pretypovanie a nie je tam žiadna rotácia, nemusel by brať celých 32 bitov.

A tu je pôvodné zadanie. Kompiler s tým nemá žiadny problém a preložil to optimálnejšie ako to s rotáciou. To sa dalo čakať. I keď ten L_INT by som čakal rovnako na 4 inštrukcie ako ten H_INT.
image.png
Zariadte sa každý ako je vám po chuti. Ja už som svoj názor vyslovil a ani na základe snorenia v ASM nevidím dôvod ho meniť.
1
Obrázok 1
Ukáž múdremu chybu a on sa ti poďakuje. Ukáž chybu hlupákovi a on sa urazí.

Používateľov profilový obrázok
budvar10
Pokročilý člen
Pokročilý člen
Príspevky: 983
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Úprava #define makra dělící 32bit val na 16bit values

Príspevok od používateľa budvar10 » 16 Apr 2021, 11:06

Union je little endian. V tomto konkrétnom prípade, pri big endian, stačí prehodiť poradie UI premenných, t.j. podmienka v kompilácii, ak by to malo byť univerzálne, ako bolo už uvedené v predchádzajúcich príspevkoch. Ale ani rotácia v tomto smere nie je úplne univerzálna na všetkých platformách. Ale OK.

Prenositeľnosť kódu je diskutabilná. Asi málokto tu nejako programuje cross-platfom a drvivá väčšina MCU je asi little endian. jirka.jirka pravdepodobne používa STM32. Vlastne aj jeho riešenie je OK, len sa mu kompilátor asi sťažuje.

@peterple
Toto som riešil na AVR kedysi dávno, šetril som na každom bajte. V avr-gcc je aj rozdiel, ktorú verziu používaš. Preklad bude iný pri rovnakých podmienkach. Asi tam máš v4.7.2. Rotácia tam nie je, lebo posuv je na celé bajty. Ale je to matematická operácia nad 4B premennou, takže ju bude brať vždy celú.
Skvelé, že si si dal námahu. :thumbup:

Mimochodom, avr-gcc má definované makro __BYTE_ORDER__ pre endian.

Možno zaujímavý článok pre C++ fanatikov k tejto téme:
https://realtime.bc.ca/articles/endian-safe.html
1
Obrázok 1

Napísať odpoveď
  • Podobné témy
    Odpovedí
    Zobrazení
    Posledný príspevok