Pytanie:
Czy praktyczna reguła „Unikaj używania zmiennoprzecinkowych” ma zastosowanie do mikrokontrolera z jednostką zmiennoprzecinkową (FPU)?
gberth
2020-04-16 20:07:45 UTC
view on stackexchange narkive permalink

Zasadą jest, że staram się unikać używania liczb zmiennoprzecinkowych w mojej bazie kodu wbudowanego systemu.

Zmienne zmiennoprzecinkowe to:

  • Wymagające dużej mocy obliczeniowej
  • Nie atomowy (może powodować problemy w aplikacji RTOS lub z przerwaniami)
  • Ich precyzja może powodować nieoczywiste zachowanie (problem z porównaniem typu float).

Ale co z mikrokontrolerem z jednostką zmiennoprzecinkową (jak STM32F4)?

Czy te obawy nadal mają zastosowanie?Czy nadal odradzałbyś używanie zmiennoprzecinkowych?

Punkty (2) i (3) nadal obowiązują.Więc nie tyle „unikaj całkowicie”, ale „używaj z otwartymi oczami”, aby uniknąć problemów z atomowością lub zawodnymi operacjami.(I nigdy nie używaj liczb zmiennoprzecinkowych jako zmiennych pętli!)
Należy wybrać MCU, aby pasował do aplikacji, zamiast projektować aplikację tak, aby pasowała do MCU.Jeśli więc możesz uniknąć operacji zmiennoprzecinkowych, możesz wybrać MCU bez FPU i prawdopodobnie obniżyć koszt swojego systemu.
@ThePhoton W przypadku STM jest to szczególnie prawdziwe, ponieważ wiele serii F1-F4 jest kompatybilnych z pinami
`_Atomic float` działa podobnie do` _Atomic int32_t`, jeśli chodzi o atomowość i porządkowanie, i działa bez blokad na zwykłych procesorach ARM.Jeśli myślisz, że zwykłe „int” jest ogólnie bezpieczne w użyciu w C, pomyśl jeszcze raz.na przykład[Programowanie MCU - optymalizacja C ++ O2 przerywa pracę w pętli] (https://electronics.stackexchange.com/a/387478).Re: atomic zmiennoprzecinkowe - obsługa kompilatora / języka jest w zasadzie taka sama jak w C ++: [Atomic double zmiennoprzecinkowy lub SSE / AVX vector load / store on x86 \ _64] (https://stackoverflow.com/q/45055402) /[C ++ 20 std :: atomic- std :: atomic.specializations] (https://stackoverflow.com/q/58680928)
Operacje na liczbach całkowitych * nie * są niepodzielne bez wyraźnych zabezpieczeń, które je tak spowodowały.Nie dzieje się to automatycznie.To sprawia, że druga uwaga jest nieważna i nie ma zastosowania.Pierwsza uwaga nie ma zastosowania, jeśli masz sprzętową jednostkę FPU.Więc patrzysz tylko na trzecią kwestię: precyzję.Jeśli potrzebujesz liczb zmiennoprzecinkowych, potrzebujesz zmiennoprzecinkowych.Nie ma to nic wspólnego z MCU.Programiści na dużych maszynach żelaznych podejmują te same decyzje z takimi samymi kompromisami w odniesieniu do precyzji.
@CodyGray: Trochę więcej myślałem o tym „nieatomowym” twierdzeniu.Zastanawiam się, czy ludzie mają na myśli, że niektóre biblioteki soft-float nie są ponownie wprowadzane, a zatem mogą się zepsuć, jeśli w trakcie obliczeń nastąpi przerwanie, nawet bez dostępu do pamięci współdzielonej (jeśli programy obsługi przerwań również używają FP lub przełączasz kontekst)?Miałoby to jakiś sens (w przypadkach / ISA, w których nie można po prostu używać miejsca na stosie dla tymczasowych, albo jest zbyt duże, albo nie ma wygodnego adresowania względnego stosu w starych 8-bitowych mikrometrach).Jeśli tak, nie jest to jasny sposób, aby to opisać, zwłaszcza w języku C.
„Może powodować nieoczywiste zachowanie” - nie jest to powód, aby nie używać pływaków, ale właściwie _uczyć się_ o ich zachowaniu.W wielu zastosowaniach pływaki _ są_ najlepiej dopasowane, jeśli możesz sobie na nie pozwolić.Stała precyzja daje po prostu kolejny zestaw nieoczywistych zachowań, które w praktycznych zastosowaniach są często znacznie gorsze.
Musisz spojrzeć na szczegółowe informacje o czasie.Nawet przy przyspieszonej FPU możesz nie mieć pojedynczego cyklu zegara mnożyć i dzielić.Zoptymalizowane obliczenia liczb całkowitych mogą nadal być szybsze i na pewno będą bardziej przenośne na tańszy sprzęt.Ale jeśli procesor jest już wybrany, majstrowanie przy procedurach całkowitych może nie mieć sensu.
@CodyGray: Większość platform oferuje gwarancje dotyczące efektów jednoczesnych operacji, które są silniejsze niż to, co wymagałoby większość języków programowania.Na przykład większość platform 32-bitowych nie wymaga, aby programiści robili nic specjalnego w celu odczytania wyrównanego 32-bitowego obiektu w sposób gwarantujący albo zwrócenie jego wartości początkowej, albo jakiejś 32-bitowej wartości, która została do niego zapisana odwtedy i gwarantujemy, że jeśli obiekt jest zapisywany tylko przez jeden wątek, a inny wątek obserwuje skutki zapisu, wszystkie przyszłe obserwacje dadzą tę wartość lub wartości zapisane po nim.
@supercat Problem z tym, że liczby całkowite / zmiennoprzecinkowe itp. Nie są atomowe, nie jest tak bardzo w sprzęcie, jak w języku C.Na przykład C uwielbia używać stosu, więc jeśli czytasz jakąś 32-bitową liczbę całkowitą za pomocą 32-bitowego procesora, może to nadal oznaczać „załaduj rejestr x ze stosu” + „odczyt rejestru x”, czyli 2 instrukcje asemblera, a nie atomowe, niezależnie od tego, czy procesor jest w stanie wykonać atomową instrukcję odczytu rejestru x, czy nie.Jeśli piszesz wszystko w asemblerze, to nie masz tego problemu, ale obecnie niewiele osób to robi.
@Lundin: Zaniedbałem scenariusz, w którym obserwacje obiektu mogą podróżować w czasie, co mogłoby wystąpić, gdyby wątek 1 czyta obiekt przez jedną lwartość, potem inną lwartość i ponownie przez pierwszą, ponowne użycie pierwszej lwartości może daćwartość odczytana wcześniej.Mea culpa o tym.Z drugiej strony myślę, że większość implementacji dla większości platform musiałaby zrobić wszystko, co w ich mocy, aby nie dotrzymać pierwszej gwarancji - że każdy odczyt da albo wartość początkową obiektu, albo jakąś wartość, która została zapisana od tamtej pory,i najogólniej powstrzymać się od takich rzeczy.
@Lundin: Szkoda, że standard języka nie zapewnia środków, dzięki którym implementacja może otrzymać „zwykły” wskaźnik do obiektu i odczytać go z dowolną zdefiniowaną semantyką w scenariuszach, w których można uzyskać do niego dostęp gdzie indziej, nawet jeśli jest stary lub nowywartości byłyby akceptowalne (nowsze prawdopodobnie preferowane, ale stare nadal tolerowane).
@supercat Bardzo ważnym scenariuszem jest: - main.c ładuje wartość rejestru z "PORTX" do rejestru procesora.- Zmiana kontekstu z ISR.- ISR zapisuje do PORTX i zwraca.- main.c zapisuje dane z rejestru procesora do PORTX i niszczy wszystko, co ISR właśnie tam zrobił.
@Lundin: Oczywiście.Jeśli kod wymaga niezawodnej atomowej sekwencji odczytu-modyfikacji-zapisu, należy napisać kod, aby ją wymusić.Jedną z moich ulubionych rzeczy jest sprzęt, który sprawia, że takie rzeczy są konieczne, zamiast używania oddzielnych adresów „set-bit” i „clear-bit”.Z drugiej strony, bardzo słaba semantyka, jak opisałem, byłaby wystarczająca do obsługi wzorca leniwego-niezmiennego-singletona z zerowym narzutem komunikacyjnym CPU, jeśli ktoś jest skłonny tolerować jedno wystąpienie wycieku na rdzeń na okres życia aplikacji.Każdy rdzeń, który odczytuje wskaźnik singletona, zobaczy wskaźnik zerowy lub ...
... adres zainicjowanej instancji (zakładając, że kod, który generuje instancję, zawiera barierę między utworzeniem singletona a publikacją jej adresu, a menedżer pamięci oferuje sposób zażądania bloku pamięci, który jest gwarantowanynie znajdować się w niczyjej pamięci podręcznej).
Biorąc pod uwagę zmienną uin32_t na jednordzeniowym, wielozadaniowym, 32-bitowym MCU (np. STM32 z RTOS).Biorąc również pod uwagę, że dwa zadania mają dostęp do tego adresu pamięci uint32_t, pierwsze jako czytnik, drugie jako pisarz. Według mojego zrozumienia, nawet przy niefortunnym przełączeniu kontekstu RTOS, stan wyścigu nie jest możliwy.Zgodziłbyś się? Tak (być może omyłkowo) zdefiniowałem atomic. Czy Twoja definicja atomic: Operation jest wykonywana w jednej instrukcji (więc nawet ISR nie może wygenerować warunku wyścigu)?Jeśli tak, czy można mieć zmienną atomową w systemie wbudowanym?
Dziewięć odpowiedzi:
Elliot Alderson
2020-04-16 20:51:52 UTC
view on stackexchange narkive permalink

Należy pamiętać, że jednostki FPU w tych mikrokontrolerach to często jednostki FPU o pojedynczej precyzji.Zmiennoprzecinkowa pojedyncza precyzja ma tylko 24-bitową mantysę (z ukrytym MSB), więc w niektórych przypadkach możesz uzyskać lepszą precyzję z 32-bitowych liczb całkowitych.

Pracowałem z użyciem arytmetyki stałoprzecinkowej, aw sytuacjach, w których dane mają ograniczony zakres dynamiczny, można osiągnąć taką samą precyzję jak zmiennoprzecinkowa pojedynczej precyzji przy użyciu 32-bitowego stałego punktu z poprawą o około rząd wielkościw czasie realizacji.Widziałem również, że kompilator wciąga spore obciążenie biblioteki dla FPU.

+1 Warto zauważyć, że Cortex M4 obsługuje spakowane typy danych i instrukcje DSP (np. Mnożenie akumulacji), które mogą dać ci ogromne ulepszenia, jeśli możesz pracować w ustalonym punkcie, nawet jeśli obecny jest FPU.Zwróć również uwagę, że STM32F7 ma podwójną precyzję FPU, ale wąskie gardła pamięci napotykasz szybciej, ponieważ nadal jest to system 32-bitowy.
+1 interesujący punkt.Nie brałem pod uwagę faktu, że FPU może obsługiwać tylko zmiennoprzecinkowe pojedynczej precyzji, a nie double.
@gberth Jest to powszechne w wielu mikro.FWIW, musisz mieć te same obawy co do liczb całkowitych.16-bitowe i 32-bitowe mikroskopy nie obsługują 64-bitowych liczb całkowitych, a niektóre nie obsługują liczb całkowitych mniejszych niż długość słowa.Wszystkie te operacje muszą być obsługiwane przez kompilator, a nie jako niepodzielne instrukcje.
awjlogan
2020-04-16 20:23:41 UTC
view on stackexchange narkive permalink

Jeśli kupujesz procesor ze sprzętową jednostką FPU, nie masz takich samych obaw o precyzję *, zachowanie ponownego wejścia itp. Śmiało i używaj ich!

Jednak kilka przemyśleń:

  • Możesz wziąć pod uwagę, że procesor może wyłączyć (duży) FPU, gdy nie jest używany, więc sprawdź, czy uruchamianie procedur FP oszczędza energię (jeśli Ci na tym zależy), zamiast robić to w oprogramowaniu.

  • W zależności od implementacji, FPU może również mieć różne rejestry w rdzeniu - czasami kompilatory mogą sprytnie je wykorzystać.

  • Nie używaj FPU jako podpórki do złego projektu oprogramowania.Na przykład, czy mógłbyś zrobić to samo ze stałym punktem i zamiast tego użyć normalnego rdzenia?

(* FPU powinno być zgodne z daną implementacją standardu, więc pamiętaj o wszelkich wynikających z tego ograniczeniach.)

+1 za zużycie energii.Nie myślałem o tym kompromisie.Dzięki.
Zauważ, że ślepe założenia, takie jak „FPU zużywa więcej energii”, często mogą wpędzić Cię w kłopoty.Emulowanie operacji FPU z procedurami stałoprzecinkowymi może skończyć się zużyciem większej liczby cykli, a tym samym większej mocy, niż gdybyś po prostu używał wbudowanej wydajnej sprzętowej jednostki FPU.I chociaż jestem pewien, że istnieją rzeczy, których nie jestem świadomy, wszystkie MCU, które widziałem ze sprzętowymi jednostkami FPU, mają dedykowane rejestry zmiennoprzecinkowe (tj. Nie pokrywają się z „rdzeniowymi” rejestrami całkowitymi).Całkowicie się jednak zgadzam, że zmiennoprzecinkowy nie powinien być używany jako pomoc w złym projekcie.Użyj odpowiedniego narzędzia do pracy.
@CodyGray - oczywiście, dlatego powiedziałem "sprawdź" :) Są szanse, że jeśli używasz FP, to FPU będzie bardziej energooszczędne, ale może też nie być, powiedzmy, jeśli wykonujesz tylko jedną operację.Micros są teraz tak zaawansowane, że istnieje wiele miejsc, w których założenia mogą Cię sprowadzić na manowce - ale to dobra zabawa!
Ralf Kleberhoff
2020-04-16 20:27:53 UTC
view on stackexchange narkive permalink

Niektóre z obaw nadal obowiązują

  • Arytmetyka zmiennoprzecinkowa jest z natury bardziej intensywna obliczeniowo niż liczba całkowita.Ale w przypadku jednostki z punktem flotacji prawdopodobnie już tego nie zauważysz, może kilka dodatkowych cykli procesora lub trochę większe zużycie energii.
  • Operacje są atomowe, więc obawy zniknęły.
  • problem precyzji / zaokrąglania / porównania nadal występuje, do dokładnie takiej samej wielkości, jak w obliczeniach programowych.

Szczególnie ten drugi może powodować bardzo nieprzyjemne problemy i zmusić do napisania nieintuicyjnego kodu, np.zawsze porównując z zakresem, nigdy nie testując równości względem ustalonej wartości.

I pamiętaj, że zmiennoprzecinkowa pojedyncza precyzja ma tylko rozdzielczość 23 bitów, więc może być konieczne zastąpienie 32-bitowej liczby całkowitej zmiennoprzecinkową podwójną precyzją.

+1 wzmianka o „nieintuicyjnym kodzie”.Nawet jeśli moje pytanie dotyczyło głównie wydajności i solidności, myślę, że ważne jest również zastanowienie się nad przejrzystością kodu.
Re „nieintuicyjny kod”, to po prostu wybranie odpowiedniego typu do tego, co robisz.Jeśli wartości nie są dokładne (np. Pomiar napięcia), dokładne porównanie jest z natury błędne.Stosowanie tolerancji nie czyni kodu nieintuicyjnym, a jedynie czyni go * poprawnym *.
Odnośnie bardziej intensywnych obliczeń: w przypadku dedykowanego sprzętu może to nadal powodować zwiększone zużycie energii.
Zgadzam się z Grahamem.Porównywanie napięcia w miliwoltach całkowitych jest prawdopodobnie błędne z dokładnie tego samego powodu.To powiedziawszy, Twój ADC prawdopodobnie generuje pomiary liczb całkowitych.
Arytmetyka zmiennoprzecinkowa jest z natury bardziej intensywna w obliczeniach niż liczba całkowita, jeśli obliczasz za pomocą liczb całkowitych.Jeśli twój problem dotyczy liczb zmiennoprzecinkowych, to wysoce dostrojony FPU będzie znacznie szybszy, wykonując całe skalowanie w oprogramowaniu;dlatego istnieją FPU.
Czy możesz podać przykład architektury, w której operacje zmiennoprzecinkowe są atomowe?Nie ma takich gwarancji ani w przypadku architektury x86, ani ARM.Mniej jestem zaznajomiony z MIPS i innymi bardziej ezoterycznymi architekturami.Ale ogólnie nie można założyć, że * dowolne * operacje będą niepodzielne, w tym operacje na liczbach całkowitych.Nawet coś tak prostego jak ładowanie lub magazyn jest często dzielone na wiele operacji ze względu na ograniczenia magistrali pamięci (w 32-bitowym MCU nie można załadować 64-bitowej wartości zmiennoprzecinkowej o podwójnej precyzji jako operacji atomowej).
jonathanjo
2020-04-16 22:05:47 UTC
view on stackexchange narkive permalink

Obliczenia są często dobre, jeśli masz FPU, a kompromisy są łatwe do zrozumienia.

Ale uważaj na wynik.Jeśli masz coś takiego jak biblioteka C, byłbyś zdumiony złożonością właściwą dla printf ("% 0.6g", x); Widziałem biblioteki, które używają malloc() wewnątrz printf () , a to nie jest coś, co chciałbyś w mikrokontrolerze.

Niestety, Standard wymaga, aby argumenty zmiennoprzecinkowe funkcji z rodziny printf były wyprowadzane przy użyciu typu „double”, który musi mieć szerokość co najmniej 48 bitów, nawet na platformach, które mają tylko 32-bitowy sprzęt zmiennoprzecinkowy.
I używa zmiennych globalnych (stan wspólny)?
Jest to bardzo dobra uwaga dotycząca konieczności uważania na rozdęte implementacje biblioteki standardowej C podczas kierowania na wbudowane MCU.Niestety nie ogranicza się to do operacji zmiennoprzecinkowych.Coś w rodzaju `printf` prawdopodobnie będzie absolutną katastrofą w bardzo ograniczonym środowisku.Na szczęście nie będziesz tego używać w produkcji.Jeśli w ogóle go użyjesz, będzie to debugowanie, gdzie wydajność nie jest krytyczna.Jeśli zależy Ci na wydajności, musisz być bardzo świadomy tego, co generuje Twój kompilator C, lub samemu napisać asm.Zwykle jednak przejmujesz się mniej niż myślisz.
Ron Beyer
2020-04-16 20:23:55 UTC
view on stackexchange narkive permalink

Szczerze mówiąc, jest to rodzaj mikrooptymalizacji, którą powinieneś wykonywać dopiero po uzyskaniu w pełni działającej bazy kodu.Niektóre MCU mają również problemy z dzieleniem, nawet w przypadku liczb całkowitych.Więc zrobienie czegoś takiego jak "pomnóż fp przez 100, zrób jakąś manipulację, podziel przez 100" może zająć znacznie więcej czasu niż tylko manipulowanie zmienną.

Tutaj pojawia się profilowanie, musisz wybrać swoje bitwy, nie ma jednej odpowiedzi.Gdy masz już działającą bazę kodu, możesz zidentyfikować wąskie gardła i dokonać selektywnej optymalizacji.Unikanie czegoś w postaci ogólnej instrukcji prowadzi do mikrooptymalizacji, które wymagają więcej czasu na kodowanie, niż w rzeczywistości oszczędzają.Optymalizacja ruchów zmiennoprzecinkowych z procedury o niskim priorytecie, która jest uruchamiana raz na godzinę, jest bezużyteczna, natomiast optymalizacja pływaków w przypadku trudnych zadań jest przydatna.

Miejmy nadzieję, że jeśli robisz stały punkt, użyjesz potęg 2 zamiast potęg 10. Zamiast "pomnożyć przez 100, operować, podzielić przez 100", powinieneś pomnożyć przez 128, operować i dzielić przez 128.Nie znam żadnego MCU, który nie może skutecznie podzielić przez 128.
@ThePhoton Tak, to był tylko szybki przykład i myślę, że dobry kompilator może zoptymalizować a / 128 do >> 8, zanim jeszcze dotrze do procesora, po prostu wybrałem dowolną wartość.
Dzielenie przez stałą nie jest dużo trudniejsze niż mnożenie.Można to zrobić w kategoriach mnożenia i przesunięć, porównaj np.kod ARM dla [tych dwóch funkcji] (https://gcc.godbolt.org/z/SMdGvH).Cóż, przynajmniej jest to prawdą, gdy masz instrukcję mnożenia (32-bitowy, 32-bitowy) → 64-bitowy.
@Ruslan: Niestety, wiele rdzeni Cortex-M0 zawiera sprzęt do mnożenia w jednym cyklu 32x32-> 32, ale nie ma wydajnego sposobu obliczania górnego słowa produktu 32x32.Wydaje mi się, że sprzęt, który mógłby wykonywać czterokrotne zwielokrotnienie, uzyskując górną lub dolną połowę wyniku, byłby tańszy i bardziej użyteczny, ale być może nie tak atrakcyjny dla działu marketingu.Uważam również za nieco ciekawe, że sprzedawcy chipów mogą żądać mnożenia w jednym cyklu lub w 32 cyklach, ale nie ma opcji mnożenia ~ 18 cykli, które byłoby prawie tak tanie jak 32-cyklowe.
Justme
2020-04-16 20:51:36 UTC
view on stackexchange narkive permalink

Zasadniczo nie musisz tego unikać, jeśli masz FPU, a twój RTOS obsługuje również przełączanie kontekstu FPU.Problem z precyzją nadal istnieje, jeśli masz FPU, czy nie.Możesz również swobodnie używać zmiennoprzecinkowych bez FPU, jeśli masz odpowiednią wydajność - sporadyczny zapis debugowania zmiennej zmiennoprzecinkowej jest po prostu dobry na Cortex-M3 bez FPU.Ale oczywiście na ograniczonym 8-bitowym MCU z małą pamięcią, obciążenie związane z użyciem nawet jednej operacji zmiennoprzecinkowej może przynieść wiele setek bajtów kodu biblioteki typu soft float, więc czasami używanie liczb zmiennoprzecinkowych nie ma sensu.

Zakładając, że operacje FPU są atomowe, jaką dodatkową pracę musi wykonać system RTOS, aby obsługiwać elementy zmiennoprzecinkowe?
Zależy to oczywiście od modelu programowania FPU - ale kiedy RTOS przełącza się z jednego zadania na drugie, musi gdzieś zapisać wszystkie rejestry CPU (struktura zadań) i przywrócić wszystkie rejestry CPU następnego zadania.W przypadku FPU przełącznik zadań musi również przechowywać i ładować rejestry FPU, więc proces przełączania zadań musi przechowywać i odtwarzać więcej rejestrów.
Jeśli procesor obsługuje leniwe przełączniki kontekstu [takie jak Cortex-M4] (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0298a/DAFJBFJF.html), RTOS niet koniecznie trzeba robić zapamiętywanie i ładowanie FPU na przełączniku kontekstu.
Lundin
2020-04-22 19:07:30 UTC
view on stackexchange narkive permalink

Wgdy nie używać zmiennoprzecinkowych

Pierwszą rzeczą, którą należy sobie uświadomić, jest to, że zmiennoprzecinkowy nie oznacza "Potrzebuję liczb dziesiętnych". To jest sytuacja, w której około 95% wszystkich potencjalnych programistów wbudowanych niewłaściwie wykorzystujących zmiennoprzecinkowe zawodzi.

Lekarstwem na to niedowierzanie jest uświadomienie sobie, że wewnętrznie program powinien używać jednostki, która ma sens dla MCU , a nie takiej, która ma sens dla ludzi.

Na przykład, jeśli mierzysz prąd w mA za pomocą 10-bitowego przetwornika ADC na chipie, wygodną jednostką do wykorzystania w oprogramowaniu są „stałe, surowe wartości ADC od 0 do 1024”. W programowaniu w C oznacza to uint16_t lub opcjonalnie uint_fast16_t . Nie int i na pewno nie float .

Używanie jednostki mA w obliczeniach oprogramowania sprzętowego jest wygodne tylko dla mózgu programisty, na wypadek gdyby nie mógł obsługiwać abstrakcyjnych jednostek. Jest to jednak niewygodne dla programu, ponieważ oznacza, że ​​trzeba przeskalować wszystkie odczyty i potencjalnie dodać niedokładność zaokrągleń. Dodatkowo kod skalujący to po prostu nadmiar narzutu. Prawdopodobnie będzie to obejmować podział, który może być bolesny dla wielu MCU.

Tak, odczytujesz prąd w mA. Ale jeśli faktycznie nie musisz drukować tego prądu na wyświetlaczu lub czymś dla człowieka, jednostka ta nie jest w rzeczywistości pomocna. Wykonaj ponowne skalowanie mA na długopisie i papierze podczas projektowania algorytmu, zamiast przeciągać go do oprogramowania układowego.


Wiedy używa się zmiennoprzecinkowych

  • Jeśli Twój MCU ma FPU i faktycznie potrzebujesz zaawansowanej matematyki , powinieneś użyć liczb zmiennoprzecinkowych. W przeciwnym razie nie powinieneś.

„Zaawansowana matematyka” niekoniecznie oznacza zaawansowaną z punktu widzenia programisty, ale z punktu widzenia oprogramowania.„Zaawansowane” obejmuje takie rzeczy, jak pierwiastki kwadratowe, geometria lub trygonometria, ogólnie użycie math.h , liczby zespolone, matematykę sztucznej inteligencji itp. Rzeczy, które byłyby trudne do wdrożenia w punkcie stałym.

Sascha
2020-04-18 20:46:58 UTC
view on stackexchange narkive permalink
  • Wykluczyłbym operacje nieatomowe z listy powodów, ponieważ twoje sposoby obsługi innych złożonych struktur można również zastosować do operacji zmiennoprzecinkowych.

  • Chciałbym również wykluczyć wymaganą moc obliczeniową z listy powodów, ponieważ jest to wysoce zależne od aplikacji i procesora.

Skoncentrujmy się na „nieoczywistych” problemach powodowanych przez zmienną precyzję float

  • Najbardziej podstawową z nich jest to, że arytmetyka FP nie jest asocjacyjna, (a + b) + c nie jest równe a + (b + c). Wyobraź sobie a = 1, b = -1, c = 1e-20. Brzmi nieszkodliwie, ale wyobraź sobie, że aplikacja używa skończonego filtru impulsów i uruchamiasz przypadek testowy, który powinien być dokładnie zerowy

  • dla mnie drugim powodem jest fakt, że operacje na liczbach całkowitych i stałych przecinkach mają przepełnienie w zakresie, którego oczekuję (ok, domyślnie nie jest włączone w C). na przykład W przypadku liczb zmiennoprzecinkowych integrator może łatwo uciec do bardzo dużych wartości, nie zauważając nikogo ...

Rich
2020-04-19 05:07:04 UTC
view on stackexchange narkive permalink

Jak wiele rzeczy, zależy to od twojego przypadku użycia.

Specjalny algorytm liczb całkowitych może być bardziej wydajny (np. w przypadku mnożenia), jeśli znasz dozwolony zakres wartości wejściowych.Gdy te wartości się rozszerzają, wszystko, co zakodowałeś, zdegeneruje się w zmiennoprzecinkowe pomnożenie bez korzyści sprzętu.

Możliwe jest radzenie sobie z powolnymi obliczeniami, które mają trudne do określenia czasy w systemie wbudowanym, uruchamiając je w pętli w tle lub nawet mając różne procesory, np.kontrola i obliczenia.



To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 4.0, w ramach której jest rozpowszechniana.
Loading...