logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

IAR -[C] pytanie dotyczące długości zmiennych w C

pawel_5 11 Wrz 2012 17:55 1626 10
  • #1 11300641
    pawel_5
    Poziom 13  
    Witam,

    Do tej pory ślepo ufałem wikipedii i wiedzę sprzegałem z kompilatorem IAR.
    Ku mojemu zaskoczeniu zmienne float i double są 4 bajtowe. long doble jest zmienną 8 bajtową, przy czym 4 z nich należą do liczb całkowitych a 4 do zmienno przecinkowych.

    W nowej zabawce zauważyłem że operuje na większych liczbach rzędu 1 000 000 000 000. 000 000

    Jak uzyskać taki rząd wielkości w języku C w IAR dla jednej zmiennej?

    Pewnie wielu z Was napisze-> HELP lub GOOGLE-> byłem tam i nic ciekawego nie wybrałem:)



    Z góry dziękuję za pomoc, przyznaję się bez bicia, że jestem cieniasem w te klocki i być może za szybko się zniechęcam, ale na swoje usprawiedliwienie mam to, że się mocno starałem...proszę o łagodny wymiar kary:)
  • #2 11300791
    krru
    Poziom 33  
    pawel_5 napisał:

    przy czym 4 z nich należą do liczb całkowitych a 4 do zmienno przecinkowych.


    Nie bardzo rozumiem to zdanie. Chodzi o cechę i mantysę?
    I jeszcze jedno pytanie - potrzebujesz liczb całkowitych czy rzeczywistych (jakieś rachunki)? W tym drugim przypadku ważne jest ile cyfr znaczących potrzebujesz, a nie jak duże są liczby.
  • #3 11300986
    pawel_5
    Poziom 13  
    Chyba będzie najlepiej jak posłużę się przykładem. Przedstawiam wartości maksymalne.

    przyklad 1.
    x=ADC/timer - x to bedzie duży ułamek np. 0,00 999 999 999 będzie użyty jako stała wyliczona na początku z przetwornika ADC i timera, timer może przyjąć nawet wartość 9 999 999 999 999

    przyklad 2.
    y=timer2*x - timer2 moze przyjmowac wartosci 9 999 999 999 999 tak na prawdę jest to 99999.9h z dokladnoscia 0,1us stad taka duza wartosc

    w przykladzie pierwszym jak i drugim mam doczynienia z duzym ulamkiem i dużą liczbą

    Nie chcialbym dzielic tego wczesniej, mam życzenie uzyskać tak duże liczby.
    Generalnie dużąliczbę muszę podzilić przez duży ułamek. Wynik y może być z dokładnością do trzech miejsc poprzecinku.

    Cytat:
    przy czym 4 z nich należą do liczb całkowitych a 4 do zmienno przecinkowych.
    dzięki long double uzyskałem powiedzmy coś takiego 999 999,999 999

    Mam nadzieję że teraz bardziej zrozumieale to ująłem.
  • Pomocny post
    #4 11301225
    tmf
    VIP Zasłużony dla elektroda
    Język C definiuje tylko minimalne wymagania dla zmiennych i ich minimalne długości. To, że na jednym CPU int będzie miał 16 bitów, nie znaczy, że na innym nie będzie miał 32 a nawet 64 bitów. Podobnie jest z float i double, one muszą być co najmniej 4 bajtowe (wykładnik + mantysa), ale to nie znaczy, że nie mogą być większe, co zwiększy precyzję. Także implementacja jest zależna od konkretnego kompilatora, a nawet procesora. Także IAR na AVR i IAR na ARM to będą dwie różne sprawy. Więc musiałbyś sprecyzować o jaki CPU chodzi. Druga sprawa - czeka cię powtórka z arytmetyki zmiennopozycyjnej. Bez tego nie ruszysz z obliczeniami o jakich piszesz. Aby podzielić liczby o jakich piszesz potrzebna by była co najmniej precyzja sięgająca 24 pozycji dziesiętnych, to już zakrawa o typy zmiennopozycyjne 128-bitowe. Coś takiego jest charakterystyczne dla CPU które mają sporawe FPU, niesądzę aby IAR miał biblioteki matematyczne realizujące obliczenia o takiej precyzji. Także raczej czeka cię przeformuowanie problemu. Z drugiej strony w dokumentacji do IAR z pewnością jest opisana reprezentacja typów.
  • #5 11301327
    pawel_5
    Poziom 13  
    tmf - to co napisałeś jest bardzo mądre. Myślę, że jestem w stanie zmienić strukturę wyliczeń. Operacje na tak dużych liczbach nie tylko zabiorą mi dużo pamięci operacyjnej, zwiększy się kod ale też procesor będzie dyszał jak człowiek na wf ze słabymi płucami po biegu na dystansie 1000 metrów.
  • Pomocny post
    #6 11301427
    tmf
    VIP Zasłużony dla elektroda
    Wbrew pozorom te liczby wcale nie są duże. W arytmetyce zmiennopozycyjnej bezwzględna wielkość liczby jest bez większego znaczenia. Każda liczba jest sprowadzana do postaci mantysa+wykładnik, a mantysa w standardowym zapisie zawsze jest liczbą z zakresu 0-0,5. Tu też leży źródło problemów. Aby przeprowadzić dowolną operację arytmetyczną w pierwszej kolejności sprowadza się wykładniki do podobnych wielkości. I teraz jeśli masz np. 10^6 i 10^-6 i chcesz podzielić jedno przez drugie zachowując precyzję to robi się kłopot. Bo żeby sprowadzić 10^-6 do zapisu 10^6 musisz zapisać mantysę o postaci 0,000000000001, co dla float jest niemożliwe i masz w efekcie 0. Ty z grubsza chcesz robić to samo, tyle, że różnica u ciebie wynosi koło 10^24 + 3 miejsca, które chcesz zachować to 10^27. Czyli musisz mieć możliwość zapisania mantysy o 27 pozycjach znaczących. Z drugiej strony zastanów się nad użyciem zapisu stałopozycyjnego - obliczenia są szybsze, a ich kod krótszy. Różne rozszerzenia C wspierają typy _Fract i _Accum, w wersji long long będziesz miał co najmniej 15.15 pozycji znaczących co powinno ci wystarczyć. Jeśli IAR to wspiera to w stosownym nagłówku będziesz miał info o rozmiarze typów.
  • Pomocny post
    #7 11301736
    krru
    Poziom 33  
    pawel_5 napisał:
    Chyba będzie najlepiej jak posłużę się przykładem. Przedstawiam wartości maksymalne.

    przyklad 1.
    x=ADC/timer - x to bedzie duży ułamek np. 0,00 999 999 999 będzie użyty jako stała wyliczona na początku z przetwornika ADC i timera, timer może przyjąć nawet wartość 9 999 999 999 999

    przyklad 2.
    y=timer2*x - timer2 moze przyjmowac wartosci 9 999 999 999 999 tak na prawdę jest to 99999.9h z dokladnoscia 0,1us stad taka duza wartosc

    w przykladzie pierwszym jak i drugim mam doczynienia z duzym ulamkiem i dużą liczbą


    Bez znaczenia - i tak tyle cyfr znaczączych nie uzyskasz. ADC (jak rozumiem jest to wynik przetwornika ADC) ma 10-16 bitów znaczących (jakieś 3-5 cyfr znaczących) i więcej nie potrzebujesz jako mantysę (powiedzmy 2-3 bity więcej by obliczenia nie wprowadzały dodatkowych błędów).
    Błąd czasu może nie jest taki oczywisty, ale nawet jak masz generator kwarcowy to i tak obliczenia dokładniejsze niż 6-7 cyfr znaczących są sztuką dla sztuki.

    Dla liczba zmiennoprzecinkowych rozmiar mantysy definiuje dokładność (liczbę cyfr znaczących), natomiast wielkość wykładnika definiuje maksymalną i minimalną wartość reprezentowywaną danym typem - i to np. 8 bitów wykładnika wystarcza na 2^127, czyli jakieś 10^40.

    Jak wspomniał kolega tmf problemem może być dodawanie/odejmowanie dwóch liczb o znacznie różniącej się wielkości - typu dodawanie jednej miliardowej do miliarda. W dokładności floata taka operacja nic nie zmieni, bo po wyrównianiu wykładników ułamek stanie się zerem.
    Mnożenie i dzielenie nie powodują problemów - nie wymagane jest wyrównanie wykładników, mantysy się mnoży/dzieli a wykładniki po prostu się dodaje/odejmuje.
  • Pomocny post
    #8 11302884
    szulat
    Poziom 23  
    pawel_5 napisał:
    99999.9h z dokladnoscia 0,1us

    więc sprawa jest prosta - twoją jednostką jest 0,1 µs a chciałbyś doliczyć do prawie 100000 godzin co daje 3600000000000000 jednostek (3,6 * 10^15), binarnie to "tylko" 52 bity, czyli do przechowywania takich wielkości wystarczy zwykły 64 bitowy long long int (int64_t lub uint64_t z #include <stdint.h>), który o dziwo jest nawet obsługiwany w avr gcc.
  • #9 11305307
    pawel_5
    Poziom 13  
    Panowie, dziękuję za mądry i serdeczny przekaz. Dużo dało mi to do myślenia. Na pewno wezmę wiele z tych uwag również w następnym projekcie pod uwagę.
    W tym zadaniu ostatecznie zrobiłem tak:

    if(zmienna_zliczajaca_us>1000000){zmienna_glowna=zmienna_zliczajaca_us/1000000;}

    Dzięki temu uzyskuje zmienne 999 999 999,9 liczone w mikrosekundach....chyba napisałem to trochę mało zrozumiale i bez sensu, ale może komuś uda się mój schemat poczęty w głowie zrozumieć. Taki pomiar jest mi potrzebny gdyż jednostkowy pomiar czasu często kończy się przed jedną sekundą, a takich pomiarów urządzenie robi miliony. Jeśli zaniedbałbym mikrosekundy, pomiary byłyby bezużyteczne. Wynik po kilku tygodniach i tak urośnie do godzin.

    Tak w dużym skrócie..Ha:)

    Dodano po 1 [minuty]:

    szulat napisał:
    pawel_5 napisał:
    99999.9h z dokladnoscia 0,1us

    więc sprawa jest prosta - twoją jednostką jest 0,1 µs a chciałbyś doliczyć do prawie 100000 godzin co daje 3600000000000000 jednostek (3,6 * 10^15), binarnie to "tylko" 52 bity, czyli do przechowywania takich wielkości wystarczy zwykły 64 bitowy long long int (int64_t lub uint64_t z #include <stdint.h>), który o dziwo jest nawet obsługiwany w avr gcc.
    to całkowita prawda, tylko niekiedy to trzeba jeszcze przerobić na ułamek.
  • Pomocny post
    #10 11305702
    hans512
    Poziom 15  
    pawel_5 napisał:
    całkowita prawda, tylko niekiedy to trzeba jeszcze przerobić na ułamek.


    A co to za problem? Operacje wykonujesz na long long (mozesz tez sprawdzic czy twoj kompilator obsluguje unsigned long long), i potem tylko manualnie "przesuwasz przecinek" by uzyskac ulamek.

    Wtedy mozesz sobie przyjac iz jedna mikrosekunda to nie jest 0.000001 a po prostu 1, a jedna sekunda to 1000000.

    Obliczenia sa wtedy znacznie latwiejsze i przyjemniejsze - odpada wtedy problem z liczbami zmiennoprzecinkowymi.

    ps. Znajac maksymalna liczbe ktora zmiescisz w long long mozesz dokladnie okreslic kiedy sie ona wyczerpie i w tym czasie wyznaczyc nowa epoke.

    nowa epoka = data zakonczenia poprzedniej (limit long long).

    http://en.wikipedia.org/wiki/Epoch_time
  • #11 11319491
    pawel_5
    Poziom 13  
    Zastosowałem się do rad kolegów i wszystko działa jak należy.
    Problem rozwiązany, zakres wiedzy i doświadczenia poszerzony.
    Dziękuję.
REKLAMA