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

[C/C++] zakres wartości dla typu float - jak to możliwe?

kaktus_c++ 20 Lut 2009 20:18 25357 23
REKLAMA
  • #1 6180018
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    Dręczy mnie jedno zagadnienie:
    typ float 4 bajtowy(czyli 32bitowy ) ma według różnych źródeł zakres wartości od -3.4E38 do 3.4E38 ,
    natomiast typ unsigned int też 4bajtowy (32bitowy) , ma zakres od 0 do 2^32.

    Czyli zakres float jest większy niż unsigned int mimo że float i unsigned int mają tyle samo bitów, a na dodatek float musi jeszcze przechowywać 1 bit na znak(+-) oraz ileś tam bitów na informację o przecinku.

    Rozumując prosto to na pierwszy rzut oka nie da się w 32 bitach zmieścić więcej niż 2^32+1 różnych wartości czyli tyle co oferuje unsigned int

    Więc czy ktoś jest w stanie to w łopatologiczny sposób wytłumaczyć?
  • REKLAMA
  • #3 6180460
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    dzięki, ale zapoznałem się już wcześniej z tym co jest na temat IEE 754 w wikipedii, dlatego spytałem o "łopatologiczne wytłumaczenie".
  • REKLAMA
  • Pomocny post
    #4 6180567
    Dżyszla
    Poziom 42  
    Posty: 7075
    Pomógł: 1095
    Ocena: 225
    liczby zmiennoprzecinkowe są małej dokładności. Tam dajmy na to 100 000 000 000 000 a 100 000 000 000 001 to zbyt mała różnica, przez co jedna z tych liczb nie istnieje w takim systemie zapisu. Stąd zapisując niejako kolejne liczby w systemie cecha-mantysa uzyskuje się ciąg, który w uproszczeniu przedstawię (w systemie dec 1 bit mantysy i 1 byt cechy) :
    0.001 0.002 0.003 0.004 ... 0.010 0.020 0.030 ..... 1000 2000 3000 4000 6000 8000 .... 90000
    Zdaje się że to jest liczba 2 bitowa w systemie dec, a z punktu widzenia stałoprzecinkowego, max liczbą 2 bitową jest 100 (10^2-1: 00..99)
    Stąd właśnie bierze się to, iż możliwą wartością jest tyle, ile wychodzi z możliwości cechy + dokładności mantysy. Rozumowanie ściśle deterministyczne, jakim są liczby stałoprzecinkowe, niestety nie ma tu zastosowania.
  • #5 6181030
    Akane
    Poziom 27  
    Posty: 638
    Pomógł: 144
    Ocena: 33
    Tłumacząc w inny sposób - im wyższa liczba, tym większy odstęp pomiędzy jednostkami. Pisząc taki konsolowy program jak poniżej (dla PC) w konsoli będą się pojawiały liczby o jeden większe od poprzedniej, aż do liczby 16777216. Kolejne dodanie jedynki do floata niczego nie zmieni, chyba że zwiększysz ziarnistość jednostki i zaczniesz dodawać 2 lub więcej.
    float f = 16500000f;
    while (1)
    {
    	print(f);
    	f++;
    }
  • REKLAMA
  • #6 6181082
    wd40
    Poziom 21  
    Posty: 538
    Pomógł: 21
    Ocena: 61
    float to typ liczby zmiennopozycyjnej czyli np 5,000*10^3
    int to typ liczby stałopozycyjnej czyli np. 5000
    Proszę zwrócić uwagę, że zmienna zmiennopozycyjna składa się z dwóch parametrów np 5,000 i 3 - będące potęgą, a stałopozycyjna jedynie z jednego, dzięki temu stałopozycyjne są dokładniejsze, a zmiennopozycyjne mają większy zakres
  • #7 6181670
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    Akane: puściłem sobie ten kod który podałeś i jestem tym zaszokowany co zobaczyłem.
    Z tego wynika że na floacie wogóle nie można polegać. Obawiałem się że właśnie tak to wygląda.
    Zrobiłem sobie też taki programik i dodając 0.1, wartość się zatrzymuje na 2097152.0000. Śmiech na sali.

    Kiedyś widziałem taki opis budowy liczb ułamkowych : przykładowo dla liczby 2 bajtowej gdzie 1 bajt odpowiada za liczbe całkowitą a drugi za ułamek i zapisujemy tylko liczby dodatnie(nie ma bitu znaku),

    binarnie zapisana liczba 00000101 10100000 wynosiłaby :
    wartość całkowita =2^0*1 + 2^1*0 + 2^2*1 (i tak dalej, reszta=0), a wartość ułamkowa=2^-1*1+2^-2*0+2^-3*1 (t tak dalej reszta=0),
    suma wartości całkowitej i ułamkowej tutaj równa byłaby 1+3+1/2+1/8

    i to miało by dla mnie sens , zakres wartości byłby wiadomo mniejszy ale można by na tym polegać.

    Chciałem sobie zrobić program który oblicza średnią temperaturę na podstawie sumy wartości dokonanych pomiarów i liczby dokonanych pomiarów . średnia=(suma)/(liczba pomiarow). ale teraz muszę to zmienić i chyba operować na unsigned int zamiast float.
  • #8 6181972
    mykhaylo
    Poziom 14  
    Posty: 102
    Pomógł: 5
    Ocena: 5
    Wątpię aby temperatura przekroczyła zakres 2097152.0000, więc nie ma co panikować. float jaki jest każdy widzi, ale nie ma co się do niego odwracać plecami :)
  • #9 6182541
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    Nie temperatura, tylko suma temperatur:).
    mierząc co 1 sek, daje 3600 pomiarów na godzinę
    jeśli temperatura ma 120 stopni to daje sumę3600*120=432000
    czyli po 5 godzinach suma przekracza 2000000. Na razie podzieliłem sobie te sumy na 1 godzinne fragmenty i trzymam w tablicy.
  • #10 6182908
    Dżyszla
    Poziom 42  
    Posty: 7075
    Pomógł: 1095
    Ocena: 225
    Przy liczbach zmiennoprzecinkowych istotna jest dokładność, jaką pozwala dany model uzyskać. I jak najbardziej mozna polegać na zmiennoprzecinkowych liczbach, ale nalezy właściwie je zastosować (nie każdy przypadek pozwala na to). A już są planowane procesory, które będą się opierać o rachunek prawdopodobieństwa, gdzie klasa dokładności niektórych obliczeń będzie mogła być umyślnie obniżona w zamian za szybkość wykonania działania.

    A zobrazuję jeszcze przypadek tak - jak lecisz na księżyc, to czy potrzebujesz wiedzieć, czy masz do przebycia 38440364 czy 38440365 cm?
  • #12 6187529
    wd40
    Poziom 21  
    Posty: 538
    Pomógł: 21
    Ocena: 61
    W przypadku sumowania takich temperatur musisz pomnożyć ilość pomiarów przez maksymalną temperaturę (a dokładniej przez wynik z dzielenia maksymalnej temperatury przez dokładność) i wtedy otrzymasz długość mantysy.
    np. ilość pomiarów 24h*60min*60sek=86400
    max. temp 200 C
    dokładność 0,5 C

    max. liczba = 86400*(200/0,5)=34.560.000
    czyli max. liczbę można zapisać na 27 bitach.

    W tym przypadku lepsza będzie liczba typu int, która ma 32 bity (można sumować nawet temp. z kilku dni) , aby za pomocą float można byłoby dokonywać podobnych operacji ich mantysa powinna mieć 27 bitów.
    Oczywiście można stworzyć własny typ float:
    1) składający się z 32 bitów: 27 mantysa + 5 cecha - dosyć trudne do realizacji
    2) lub składający się z 40 bitów: 32 matysa + 8 cecha - operacje matematyczne w zasadzie nie odbiegałyby od operacji dla typu int

    Pomimo tego, co wyszło w moim przykładzie obecnie dąży się do stosowania liczb float o dużej długości (double) o ile pozwala na to procesor i pamięć.
  • #13 6187533
    2lero
    Poziom 12  
    Posty: 67
    Pomógł: 5
    Ocena: 8
    Taką rozpiętość można uzyskać nawet na jednym bicie!!! Np przyjmując taki kod:
    0 - oznacza -3.4E38
    1 - oznacza 3.4E38

    na 2 bitach można zwiększyć dokładność. Np.
    00 - oznacza -3.4E38
    01 - oznacza 0
    10 - oznacza 1
    11 - oznacza 3.4E38

    itp
  • #14 6189125
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    Dr.Vee : Ten link który podałeś jest "trochę" obszerny, ale zapewne wyczerpujący. Jakbym to przeczytał i pojął to zapewne zrobiłbym doktorat i nazywał się Dr. Kaktus. :)

    O tym że float może się mylić to znaczy że zaokrągla sobie ułamki i czasem może to być przyczyną błędnego wyniku zdawałem sobie sprawę od dawna, ale mimo że przeczytałem kilka książek o programowaniu to nie przypominam sobie żebym natrafił na wzmiankę że czasem x+1==x, to mnie zaskoczyło.

    wd40: ten program który tworzę jest na mikrokontroler AVR z pamięcią 8 kbajt. Więc operowanie na typie int jest uzasadnione dlatego że typ float nie jest tam ponoć zaimplementowany sprzętowo i jego użycie zwiększa bardzo objętość programu. Ale wyrobiłem się z użyciem tablicy floatów i na razie program się mieści i działa ok.

    2lero: tak rzeczywiście w uproszczeniu sprawa wygląda

    pozdrawiam
  • #15 6189201
    Dżyszla
    Poziom 42  
    Posty: 7075
    Pomógł: 1095
    Ocena: 225
    kaktus_c++ napisał:
    O tym że float może się mylić to znaczy że zaokrągla sobie ułamki i czasem może to być przyczyną błędnego wyniku zdawałem sobie sprawę od dawna, ale mimo że przeczytałem kilka książek o programowaniu to nie przypominam sobie żebym natrafił na wzmiankę że czasem x+1==x, to mnie zaskoczyło.
    A czymże to innym, jak nie błędem zaokrąglenia? :) FP to tak, jakbyś liczył jakieś wartości w fizyce, gdzie poza wartością (mantysa) istotne są jeszcze jednostki (cecha)... Tak 0,26666g+0,23333g=0,5g; jak i 0,26666kg+0,23333kg=0,5kg :)
  • #16 6190209
    wd40
    Poziom 21  
    Posty: 538
    Pomógł: 21
    Ocena: 61
    Już nie wiem o co Tobie chodzi, przecież w pierwszym poście sam sobie odpowiedziałeś:
    kaktus_c++ napisał:
    Rozumując prosto to na pierwszy rzut oka nie da się w 32 bitach zmieścić więcej niż 2^32+1 różnych wartości czyli tyle co oferuje unsigned int

    Ze wskazaniem na RÓŻNYCH WARTOŚCI!
    Co nie znaczy, że przy pomocy float nie da się zapisać większych liczb.

    Uint ma 32 bitową mantysę i 0 bitową cechę, pozwala na zapis 2^32 różnych wartości, a największą liczbą jest 2^32-1
    float IEE 754 (czyli single) ma 24 bitową mantysę (razem ze znakiem) i 8 bitową cechę, pozwala na zapis również 2^32 różnych wartości, z tym, że przy stałej cesze można zapisać tylko 2^24 różnych wartości, natomiast największa liczba to mniej więcej 2^24 * 2^127.
  • #17 6190974
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Odchodząc trochę od tematu - po co Ci suma temperatur mierzonych co sekundę? Jeśli sumujesz je w celu uśredniania, to chyba nie potrzebujesz aż pięciogodzinnych pomiarów (z taką częstotliwością) żeby wyznaczyć średnią?

    PS. to nie "float się czasem myli" - to programista nie wie, jak działa arytmetyka zmiennoprzecinkowa ;)

    Pozdrawiam,
    Dr.Vee
  • REKLAMA
  • #18 6191055
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    WD40 : Doceniam Twój wkład w dyskusję. Chodziło mi o to żeby wyjaśnić jak to możliwe że float ma taki duży zakres wartości. I już teraz wiem.
    Sprawdziłem też że dla pewnego zakresu czyli do wartości 2097152.0000 float oferuje dokładność która jest dla mnie w tym programie który teraz tworzę zadowalająca. I to wszystko.

    Dr.Vee: To jest program monitorujący wędzarnię, a w przyszłości po przeróbkach będzie też służył do sterowania oknami w szklarni. W przypadku wędzarni po dorzuceniu drewna temperatura się dość szybko zmienia. Więc termometr mierzy ją co 1 sek. Wystarczają mi 1 godzinne pomiary, które umieszczam w tablicy. To pozwala mi na liczenie średniej zarówno z ostatniej godziny jak i z kilku godzin, w zależności co chcę wiedzieć. No i można też tworzyć na tej podstawie wykresy. Większych tablic nie mogę tworzyć ze względu na ograniczoną pamięć.

    pozdrawiam.
  • #19 6193692
    Dżyszla
    Poziom 42  
    Posty: 7075
    Pomógł: 1095
    Ocena: 225
    Jeśli potrzebujesz tą sumę tylko do średniej, to nie przejmowałbym się aż tak dokładnością - nawet różnica rzędu 1-2 stopni dla pojedynczego pomiaru nie dość, że nic nie wniesie wynik to jeszcze i tak pewnie zostanie zniwelowana drugim pomiarem zsumowanym z błędem in minus.
    A generalnie to przy takich dynamicznych zmianach, o jakich piszesz, to jakie znaczenie ma w ogóle tak długa średnia?
    Co więcej - na potrzeby średniej wcale nie musisz odnotowywać tak gęsto pomiarów. Częsty pomiar może być przydatny do monitorowania stanu bieżącego, a na potrzeby statystyczne wystarczy wziąć co 2-3 pomiar.
  • #20 6196192
    arturt134
    Poziom 27  
    Posty: 792
    Pomógł: 76
    Ocena: 24
    W wędzarni (sam czasami coś uwędzę, więc mam informacje z pierwsze ręki) temperatura chwilowa może się wahać dosyć mocno, ale średnia nie zmienia się za szybko. Komora wędzalnicza i kilka (naście) kilogramów mięsa, które w niej wisi zapewnia układowi bardzo dużą bezwładność cieplną. Ja na twoim miejscu mierzyłbym co 10...60 sekund.

    A co do liczb zmiennoprzecinkowych, to zwróć uwagę że samo przekształcenie sygnału nieelektrycznego (temperatury) w sygnał elektryczny (napięcie) a następnie pomiar tego napięcia przetwornikiem AD powoduje WIELOKROTNIE większy błąd niż błąd zaokrąglenia liczby zmiennoprzecinkowej.
  • #21 6196440
    kaktus_c++
    Poziom 18  
    Posty: 231
    Pomógł: 26
    Ocena: 3
    Dżyszla : masz rację że nie muszę tak często odnotowywać tych pomiarów, rozważałem różne opcje.

    artur134 : Masz rację z tym co napisałeś. Ale ja do pomiaru używam termometru ds18S20, który jest układem scalonym, mierzy z dokładnością 0.5 stopnia i przesyła wynik cyfrowo do mikrokontrolera. To jest termometr cyfrowy. Nie ma tutaj opcji że jakieś zakłócenia zafałszują pomiar, bez względu na długość kabla (do 30m). Po odebraniu pakietu danych z czujnika program sprawdza sumę kontrolną i jeśli coś jest nie tak to pomiar będzie odrzucony . A termometr dokonuje pomiaru w czasie mniejszym niż 1s. Więc skoro mogę mierzyć co 1s, to robię to.

    Pozdrawiam.
  • #22 6201054
    arturt134
    Poziom 27  
    Posty: 792
    Pomógł: 76
    Ocena: 24
    Niby tak, ale jeżeli weźmiesz 600 wyników 1 sekundowych lub 10 wyników 1 minutowych i policzysz średnią, to i tak będzie ona taka sama, albo mało różniąca się.
  • #23 6202499
    wd40
    Poziom 21  
    Posty: 538
    Pomógł: 21
    Ocena: 61
    Zapominacie o błędach pomiaru. Przy 600 pomiarach, 1 pomiar uszkodzony niewiele wpływa na średnią, przy 10-u 1 zły to błąd około 10%
  • #24 6205873
    arturt134
    Poziom 27  
    Posty: 792
    Pomógł: 76
    Ocena: 24
    Albo robisz urządzenie pomiarowe albo nie. Błąd gruby, rzędu kilkunastu stopni nie ma prawa się pojawić.

Podsumowanie tematu

✨ Dyskusja dotyczy różnicy zakresu wartości między typem float a unsigned int w języku C/C++, mimo że oba mają 32 bity. Float, zgodnie ze standardem IEEE 754, reprezentuje liczby zmiennoprzecinkowe złożone z mantysy i cechy (eksponentu), co pozwala na znacznie większy zakres wartości niż unsigned int, który jest liczbą całkowitą o stałej precyzji. Float ma mniejszą dokładność i zmienną precyzję, co powoduje, że różnice między bardzo bliskimi wartościami mogą być niewidoczne (np. x+1 == x dla dużych wartości). W praktyce oznacza to, że float może reprezentować liczby od około -3.4E38 do 3.4E38, ale z ograniczoną precyzją, podczas gdy unsigned int ma zakres od 0 do 2^32-1 z pełną dokładnością. W dyskusji poruszono także praktyczne aspekty stosowania float w mikrokontrolerach AVR, gdzie brak sprzętowej obsługi float wpływa na rozmiar programu. Omówiono również zastosowanie float do sumowania i uśredniania pomiarów temperatury z czujnika cyfrowego ds18S20, podkreślając, że dla określonych zakresów i dokładności float jest wystarczający, a częstotliwość pomiarów można dostosować do potrzeb, by uniknąć problemów z precyzją i pamięcią. Wskazano, że błędy zaokrągleń są naturalne dla liczb zmiennoprzecinkowych i należy je uwzględniać w projektowaniu algorytmów. Polecono literaturę dotyczącą arytmetyki zmiennoprzecinkowej, m.in. artykuł "What Every Computer Scientist Should Know About Floating-Point Arithmetic".
Wygenerowane przez model językowy.
REKLAMA