Výpočet float a převod na unsigned int – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Výpočet float a převod na unsigned int – C / C++ – Fórum – Programujte.comVýpočet float a převod na unsigned int – C / C++ – Fórum – Programujte.com

 

21. 2. 2018   #1
-
0
-

   

float voltage = 30.0;

unsigned int dac = voltage * 65535 / 30.0;

výsledek je dac = 0x11FF.  Datový typ int má 2 Byte. Pokud použiji datový typ long (má velikost 4 Byte) k chybě nedojde.

Očekával jsem, že celý výpočet bude proveden jako float a pak bude celočíselná část výsledku vložena do dac. Jaká platí pravidla pro tento případ?

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
21. 2. 2018   #2
-
0
-

Tady si mozna mel prihodit i platformu, kompilator a jestli to ma hardwarove FPU ci softwarova emulace (ci ma hw ale pouziva se jen softwarova FPU)....

Napriklad Arduino Mega s avr-gcc/g++:

void setup() {
  Serial.begin(115200);
}

void loop() {
  for (volatile float voltage = 0.0; voltage<30.1; voltage+=1){
    unsigned int  dac = voltage*65535/30.0;
    Serial.print(dac, HEX); Serial.print(' ');
    delay(500);
  }
  delay (5000);
  Serial.println();
}

 Vystup:

0 888 1111 1999 2222 2AAA 3333 3BBB 4444 4CCC 5555 5DDD 6666 6EEE 7777 7FFF 8888 9110 9999 A221 AAAA B332 BBBB C443 CCCC D554 DDDD E665 EEEE F776 FFFF 
Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
21. 2. 2018   #3
-
0
-

EW 8051 ver. 7.x (IAR Systems), zkompilováno jako debug a zkoušeno na emulátoru PICE 52 (výr. Python) pro AT89C5131.

Je zvláštní, že do většího long to uloží správně.

hu

Nahlásit jako SPAM
IP: 193.86.81.–
KIIV
~ Moderátor
+43
God of flame
22. 2. 2018   #4
-
0
-

No jeste bych zkusil treba  dac = voltage * 2184.53333; No a kdyz to nepomuze, treba pouzijes (long)(voltage * 65535.0 / 30.0). Nicmene bych se nebal ani toho long dac. Stejne je tam uplne nejnarocnejsi pocitani s plovouci carkou.

Kazdopadne tohle zni uz jako chyba kompilatoru. Nicmene nekdy se chovaji divne i emulatory, ale v tomhle pripade by to byt asi nemelo.

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
22. 2. 2018   #5
-
0
-

 dac = voltage * 2184.53333; a výsledek: 0x0F00 ... ještě jsem zkusil  

float f = voltage * 65535 / 30.0;
dac = f;

f = 65535, po provedení dac = f se změní na 9,183e-41 a dac = 0x11FF.  Explicitní přetypování výsledku nemá žádný vliv.

Nesmysly vznikají i v polovině rozsahu pro voltage = 15.0. Pokud zkusím použít signed int dac, výsledek je 0x7FFF (překvapivě správný).

Převod na signed nebo unsigned long je bez problémů.

Kde jen soudruzi ze Švédska udělali chybu? :)

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
22. 2. 2018   #6
-
0
-

#5 hlucheucho
no jestli se pri vykonani  dac = f  zmeni i f, tak to uz je bud spatnej kompilator, nebo spatnej simulator, nebo FP libka. Ikdyz kdo vi, treba ten procesor ma nejakou zaludnost, ktera ovlivni i jine registry, nez kompilator ceka a prepise se i neco, co nema.

Proste bych se radeji uplne vyhnul floatu, jelikoz vypocty s nim jsou na procesorech bez FPU priserne pomaly.

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
22. 2. 2018   #7
-
0
-

Nejpravděpodobnější se mi jeví chyba překladače nebo knihovny. Psát tech. podpoře nemá smysl, za 10 let vyšlo několik verzí, odkázali by mne na pořízení nejnovější verze.

Ten výpočet představuje asi 400 mikrosekund, což není kritické. Navíc pro DAC ho stačí provést jen při změně hodnoty.

Ještě mne napadlo použít long pro práci s pevnou des. čárkou. Dalo by se tak pracovat asi na 4 des. místa.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
22. 2. 2018   #8
-
0
-

Pomalý float musím rozporovat. Výpočet s použitím unsigned long s pevnou des. čárkou na 3 des. místa trval 976 mikrosekund, float výpočet 331 mikrosekund.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
22. 2. 2018   #9
-
0
-

#8 hlucheucho
zkousim na Arduinu (AVR), ktery ani nema instrukci pro deleni (narozdil od x51):

  {
    static uint32_t test = 0;
    auto ts1 = micros();
    for (uint32_t i = 0; i<1000; ++i) { test += i*5UL; }
    auto ts2 = micros();
    Serial.println(ts2-ts1);    // -> 696 (us)
    test = 0;
  }

  {
    static float test = 0.0;
    auto ts1 = micros();
    for (float i = 0.0; i<1000.0; i+=1.0) { test += i*5.0; }
    auto ts2 = micros();
    Serial.println(ts2-ts1);    // -> 23820 (us)
    test = 0;
  }
  
  {
    static uint32_t test = 0;
    auto ts1 = micros();
    for (uint32_t i = 0; i<1000; ++i) { test += i/5UL; }
    auto ts2 = micros();
    Serial.println(ts2-ts1);    // -> 38276 (us)
    test = 0;
  }

  {
    static float test = 0.0;
    auto ts1 = micros();
    for (float i = 0.0; i<1000.0; i+=1.0) { test += i/5.0; }
    auto ts2 = micros();
    Serial.println(ts2-ts1);    // -> 45548 (us)
    test = 0;
  }

Ale je to procesor, bez FPU a dokonce bez instrukce pro deleni. Takze mas pravdu, ze jakykoliv vypocty s pouzitim deleni, jsou priserne pomaly

Ale zase vsechno delam 1000x. Si moc nedokazu predstavit, aby jednoduchej vypocet jako si ukazoval zabiral temer milisekundu.

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
MilanL+1
Grafoman
22. 2. 2018   #10
-
0
-

#8 hlucheucho
ahoj, jen takovej nápad co zkusit menší redukci toho zlomku ve výpočtu?

z  voltage * 65535 / 30.0 na voltage * 13107/6.0 nebo použít jen násobení voltage *  2184.5 (=65535/30)
třeba alespon na odzkoušení hodnot.

Nahlásit jako SPAM
IP: 185.112.167.–
23. 2. 2018   #11
-
0
-

#10 MilanL
to nebude mít žádný efekt, jak jsem napsal v #5 bude výsledek 0x0F00. Dále jsem v tom samém příspěvku napsal, že k problému dochází až při konverzi na unsigned int, float výsledek je správný.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
semo
~ Anonymní uživatel
4 příspěvky
23. 2. 2018   #12
-
0
-

#11 hlucheucho
dac = voltage * 2184.53333  (z Tvého příspěvku)

se rovná 65535.9999 (při voltage 30.0). To už vypadá poměrně nebezpečně pro 2B, ne?

Otázka je, jak se tam ty trojky na konci 2184.533333 dostaly. Pravděpodobně jsi použil 65536/30. 

Nahlásit jako SPAM
IP: 77.48.233.–
23. 2. 2018   #13
-
0
-

#12 semo
jak se tam dostaly se zeptej KIIV. Dále jsem uvedl v #5, že k chybě dochází i v polovině rozsahu pro voltage = 15.0, výsledek by pak měl být 0x7FFF, při přetečení o 1 pak 0x8000. Tím je vyloučeno, že by to byl "produkt" přetečení datového typu. Jinak při přetečení bych očekával, že se zapíše 16 bitů od LSB počínaje  a vyšší řády po MSB včetně se nenávratně ztratí.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
semo
~ Anonymní uživatel
4 příspěvky
23. 2. 2018   #14
-
0
-

Já bych použil třeba něco jako:

dac = floor(0.5 + (std::max(0.0, std::min(1.0f, voltage / 30.0f)) * (65535.0f - FLT_EPSILON)));

Nahlásit jako SPAM
IP: 77.48.233.–
23. 2. 2018   #15
-
0
-

#9 KIIV
Samotný součin u long je asi 6x rychlejší, než u float. To platí v případě, že mu dám konstantu 2184,5. Nejpomalejší je dělení. U celých čísel je to citelně horší než u float. Float je v semilogaritmickém tvaru - asi to má něco společného s logaritmy. Ještě si vzpomínám na poučku o logaritmu součinu a logaritmu podílu. To by mohlo vysvětlovat rychlejší dělení.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
23. 2. 2018   #16
-
0
-

Pokusy s unsigned long a pevnou des. čárkou na 3 des. místa ukázaly:

1. u konstanty nemusí 3 desetinná místa vyhovovat. Příklad je konstanta 2184,5. Pokud se chci vyhnout dělení výsledku, protože je každé číslo uloženo jako 1000-násobek, musel bych použít 2,1845 tedy 4 des. místa. Omezení na 3 des. místa pak znemožňuje dosažení Full Scale DAC.

2. Datový typ je citlivý na přetečení, konstanty v podobě zlomku sice zvýší přesnost, ale je třeba zlomek nejdříve krátit a pak teprve použít v programu.

3. Pokud z nějakého důvodu dojde na dělení, bude to o hodně pomalejší než float. Toto se týká i použití konstant v podobě zlomku.

Chybný převod float na unsigned int si vysvětluji tím, že "soudruzi ze Švédska někde udělali chybu." Ta je možná u novějších verzí překladače opravena. 

Nakonec jsem použil výpočet float a převod na signed long, kde mohu pro požadovaný rozsah 0 - 65535 detekovat přetečení nebo podtečení o 1.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
semo
~ Anonymní uživatel
4 příspěvky
23. 2. 2018   #17
-
0
-

Ještě se chci zeptat:

1) Jsou ty floaty standardu IEEE 754 ?

2) Suffix f nepíšeš naschvál, nebo počítáš v doublech, nebo to double nepodporuje?

Nahlásit jako SPAM
IP: 77.48.233.–
KIIV
~ Moderátor
+43
God of flame
23. 2. 2018   #18
-
0
-

#13 hlucheucho
Jak se tam dostaly trojky? Automaticke psani 65536 misto 65535.

#16 hlucheucho
Holt na AVR to vychazelo lip. Kazdopadne jsou longy blby vzdy na 8b procesorech

#17 semo
Double bude pravdepodobne alias pro float. Tak je to i u avr-gcc.

----

Jinak u AVR se u 8b deleni pouzivala aproximace pomoci nasobeni. Vysledek stejne skonci ve dvou registrech, a tak stacilo vynasobit cislo tusim necim jako 51 a v hornim bajtu pak vysel vysledek deleni.

U x51 je to na 8b zbytecne, protoze je tam instrukce pro deleni. Proto me prekvapuje, ze je to tak pomale. Jako by stejne deleni jen emulovali.

Nahlásit jako SPAM
IP: 93.91.151.–
Program vždy dělá to co naprogramujete, ne to co chcete...
23. 2. 2018   #19
-
0
-

#18 KIIV
S tím 65536 jsem to čekal, stává se mi to taky. Správně předpokládáš, že double a float jsou stejné. Ona ta instrukce pro dělení nemusí být přínosem v okamžiku, kdy máš dělit vícebytová čísla. Kdysi jsem to v assembleru zkoušel a jediný, co jsem dokázal, bylo udělat binární dělení tak, jak dělíš ručně dekadicky na papíře - stejný algoritmus funguje jak pro dekadickou tak dvojkovou soustavu. Jak využít instrukci DIV v tomto případě jsem neobjevil.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
23. 2. 2018   #20
-
0
-

#17 semo
In the 8051 IAR C/C++ Compiler, floating-point values are represented in standard
IEEE 754 format.

Používat suffix je zbytečné když všechny floating-point types jsou stejný.

Nahlásit jako SPAM
IP: 195.178.67.–
semo
~ Anonymní uživatel
4 příspěvky
23. 2. 2018   #21
-
0
-

#20 hlucheucho
Jasně, to jen, kdybys chtěl ten kód někam přenášet. Na PC už je double samozřejmě větší a házelo by to warningy.

Ohledně toho standardu: chtěl jsem tě odkázat na tohle: http://stereopsis.com/FPU.html. Je tam hodně rychlé přetypování založené na formátu floatu. Neříkám, že pro tenhle případ, ale někdy by se to mohlo hodit. Nicméně je to tam pro double a nemůžu najít verzi pro float. A nechce se mi ji teď vymýšlet. Takže asi nic :-).

Nahlásit jako SPAM
IP: 77.48.233.–
23. 2. 2018   #22
-
0
-

Ještě mne napadlo zkusit trial verzi. K dispozici je verze 10.10.1. Obsah f změní taky, zřejmě při optimalizaci vychází z toho, že jeho obsah už nebude potřeba. Výsledek konverze na unsigned int je očekávaných 0x7FFF pro voltage = 15.0 a 0xFFFF pro voltage = 30.0.  


#include <ioAT89C5131.h>

int main()
{
  float voltage = 30.0;
  float f = voltage * 2184.5;
  unsigned int dac = (unsigned int)f;
  P2 = 0xff & dac;
  return 0;
}

Problém jde vyřešit za 1200 euro = koupit aktuální verzi EW 8051. Naproti tomu použití long nestojí ani cent. Zde rozhoduje "ten co drží kasu" a to já nejsem :(. 

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 99 hostů

Podobná vlákna

Převod 32 Int na float — založil Jakub Kohout

Float to int — založil Atmega_uset

Int vs. unsigned int — založil Lump

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý