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

Pętla w C/C++: zmniejszanie wartości zmiennoprzecinkowych z1 i z2

nowicjusz12345 10 Sie 2011 08:45 2998 22
REKLAMA
  • #1 9809792
    nowicjusz12345
    Poziom 9  
    Posty: 36
    Witam
    Mam do napisania pętlę która zmniejsza wartości z1 o 4 i z2 o 1 do czasu gdy z1 będzie równe z2, przy czym z1 > z2 np. jak dam z1=42, z2= 36 to pętla szybko zostanie przerwana przy wartości 34. np.

    Cytat:


    double z1,z2;

    do
    {
    z1 = z1 - 4;
    z2 = z2 - 1;
    }
    while(z1!=z2);



    Problem polega na tym ze jak to zrobić dla liczb zmiennoprzecinkowych? Tak aby za z1 i z2 wczytywać dowolne wartości całkowite i z1 > z2


    Próbowałem w ten sposób ale program się zawiesza

    Cytat:


    double z1,z2;

    do
    {
    z1 = z1 - 0.004;
    z2 = z2 - 0.001;
    }
    while(z1!=z2);



    Czy mógłby ktoś podsunąć pomysł jak to rozgryść?
  • REKLAMA
  • #2 9809919
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    Problem jest z reprezentacją liczb zmiennoprzecinkowych, tak naprawdę często:

    Kod: text
    Zaloguj się, aby zobaczyć kod


    daje false.

    Mógłbyś założyć jakąś dokładność i zamiast warunku
    Kod: text
    Zaloguj się, aby zobaczyć kod
    mógłbyś dać:
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #3 9809939
    nowicjusz12345
    Poziom 9  
    Posty: 36
    gaskoin napisał:


    Mógłbyś założyć jakąś dokładność i zamiast warunku
    Kod: text
    Zaloguj się, aby zobaczyć kod
    mógłbyś dać:
    Kod: text
    Zaloguj się, aby zobaczyć kod



    i co to będzie to d? jakaś stała po przecinku, jak to zapisać?
  • REKLAMA
  • #4 9809956
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    d to będzie jakaś stała po przecinku - musisz dobrać eksperymentalnie bo to właściwie zależy z jaką dokładnością ma być wynik.

    w kodzie może to być:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Interpretacja czegoś takiego jest dość prosta i łatwo przedstawić to jako otoczenie punktu. Wyobraź sobie, że masz na osi punkt P, oraz jego otoczenie d (czyli obszar w okół tego punktu o długości d w jedną stronę i d w drugą stronę czyli taki ograniczony przedziałem [P - b, P + b]) i dopóki punkt P2 jest poza tym obszarem to przyjmuje się, że jest różny od P, jeżeli jest w tym obszarze, to jest równy P. Takie ot przybliżenie.

    PS - nie mam jak narysować osi
  • REKLAMA
  • #5 9809961
    xanio
    Poziom 27  
    Posty: 933
    Pomógł: 68
    Ocena: 55
    Po pierwsze, poczytaj o reprezentacji liczb zmiennoprzecinkowych. Nie dajesz całości programu więc ciężko stwierdzić jednoznacznie ale może się okazać, że dwie liczby zmiennoprzecinkowe po szeregu operacji arytmetycznych, różnią się na którymśtam miejscu po przecinku.

    Po drugie, nie wiem dlaczego robisz pętlę
    
    do {
    ...
    } while (z1!=z2)
    

    a w środku odejmujesz od tych liczb różne wartości. W ten sposób większa liczba w pewnym momencie stanie się mniejsza a Ty dalej będziesz odejmował w nieskończoność. Wystarczy, że dasz warunek while (z1>z2) i będzie OK.
  • #6 9809964
    beluosus
    Poziom 25  
    Posty: 362
    Pomógł: 123
    Ocena: 18
    Tak, d to jakieś ziarno, w Twoim wypadku chyba optymalnie będzie jeśli d = 001. Częściej jednak porównanie liczb zmiennoprzecinkowych wykonuje się tak:
    Kod: text
    Zaloguj się, aby zobaczyć kod

    Będzie to szybsze (przy optymalizacji kodu) ze względu na fakt, że fabs jest pojedynczą instrukcją procesora.

    PS
    W tym akurat problemie lepszym rozwiązaniem jest podane przez @xanio. :)
  • #7 9809965
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    xanio napisał:
    Wystarczy, że dasz warunek while (z1>z2) i będzie OK.


    Też prawda, nie doczytałem warunków :) Wtedy nie trzeba pajacować z przedziałami - ale to Ci się chociaż może przyda na przyszłośc.
  • #8 9809967
    nowicjusz12345
    Poziom 9  
    Posty: 36
    ok dzięki za wytłumaczenie będe próbował potem bo teraz nie mam czasu
  • REKLAMA
  • #9 9810164
    Konto nie istnieje
    Konto nie istnieje  
  • #11 9810276
    Konto nie istnieje
    Konto nie istnieje  
  • #12 9810341
    Konto nie istnieje
    Konto nie istnieje  
  • #13 9810466
    Konto nie istnieje
    Konto nie istnieje  
  • #14 9810592
    Konto nie istnieje
    Konto nie istnieje  
  • #15 9810833
    Konto nie istnieje
    Konto nie istnieje  
  • #16 9810866
    Konto nie istnieje
    Konto nie istnieje  
  • #17 9813534
    nowicjusz12345
    Poziom 9  
    Posty: 36
    Witam ponownie chce wrócić do tematu. Cały program ma działać w ten sposób że mam 6 zmiennych z1>z2>z3>z4>z5>z6 i na poczatku zmniejszane sa z1 4X i z2 1X do mometu gdy beda równe a potem z1 i z2 5x , kolejno z1,z2,z3 5x; z1, z2, z3, z4 5x oraz z1,z2,z3,z4,z5,z6 5x az do monentu gdy wszystkie zmienne osiagna 0. dla kazdej petli licze ilosc przejsc a nastepnie ma powstac wykres(słupkowy) poszczególnych zadan z. I tak zaczołem pisac tak jak ponizej, wyswietla mi słupki z 1 i drugiej petli ale nie z 3 nie wiem dlaczego? moze to zła metoda?? za duzo obliczen?? Prosze o odp

    Cytat:

    double l1=0;
    while(z1 > z2)
    {
    z1 = z1 - 0.0004;
    z2 = z2 - 0.0001;
    l1++;
    }
    l1 = l1/10000;
    if(z1<=z2)
    {
    chart1->Series["Z1"]->Points->AddXY(1, 0, l1);
    chart1->Series["Z2"]->Points->AddXY(1, 0, l1);
    }



    double l2=0;
    while(z2 > z3)
    {
    z1 = z1 - 0.0005;
    z2 = z2 - 0.0005;
    l2++;
    }
    l2 = (l2/10000)*2;
    l2 = l1 + l2;
    if(z2<=z3)
    {
    chart1->Series["Z1,Z2"]->Points->AddXY(1, l1, l2);
    }



    double l3=0;
    while(z3 > z4)
    {
    z1 = z1 - 0.0005;
    z2 = z2 - 0.0005;
    z3 = z3 - 0.0005;
    l3++;
    }

    l3 = (l3/10000)*3;
    l3 = l2 + l3;

    if(z3<=z4)
    {
    chart1->Series["Z1,Z2,Z3"]->Points->AddXY(1, l2, l3);

    }






  • #18 9813696
    Konto nie istnieje
    Konto nie istnieje  
  • #19 9815655
    nowicjusz12345
    Poziom 9  
    Posty: 36
    Wiec co proponujesz zamiast tej petli?
  • #20 9827526
    Eagle
    Poziom 24  
    Posty: 536
    Pomógł: 57
    Ocena: 31
    Szkoda, że pod postem nie ma przycisku komentuj, wówczas bym go użył.
    Cytat:
    A Ty rozumiesz skąd się biorą problemy wynikające z otrzymywania nieco innych rezultatów w FPU na procesorach AMD i Intel? ;) Masz wgląd w mikrokod tych procesorów? Myślisz, że mógłbyś taki uzyskać? Nawet jeśli pracujesz dla Intel/AMD i jesteś w stanie to określić, jesteś pewien, że procesor z jeszcze inną jednostką FPU, który zostanie wyprodukowany jutro, też będzie wspierany przez Twój kod?



    Nie trzeba pracować w intelu wystarczy zapytać google jak działają liczby zmienno przecinkowe i skąd różnice. Podstawą jest standard IEEE-754, to on opisuje jak zachowują się przechowywane liczby i operacje na nich.
    Główny powód różnicy to skończona dokładność zapisu tych liczb. Liczby te zaprojektowano tak aby można było przechowywać duże wartości ( np.:odległość do księżyca) i bardzo małe ( średnica atomu). Gdyby zastosować konwencjonalne sposoby przechowywania liczby takie jak stało przecinkowe to ilość miejsca zajmowanego przez taką liczbę była by głównie marnowana.
    A teraz czas na analizę systemu IEEE-754

    niech :
    float k = 5;
    w pamięci pod adresem &k będzie :
    0x40A00000
    Patrząc na standard dowiadujemy się :
    S = Bit 31 znak 0: liczba dodatnia ( zgadza się)
    E = Bity 30-23 – wykładnik = 0x81= 129(dec) ; bias dla liczb [0xFE -0x7F ] = 127
    M = Bity 22- 0 - mantysa = 0x200000; dodając pierwszy przed przecinkiem ukryty bit (1) i przedstawiając binarnie daje 1,01000000000000000000000 ( BIN) stało przecinkowo
    Obliczając wartość tej liczby na dec postępujemy następująco dla pierwszej liczby po przecinku wartość jest
    1/(2^1) *0 = 1/2 *0 = 0
    1/(2^2) *1 = 1/4 *1 = 0,25
    1/(2^3)*0 = 1/8 *0 = 0
    (...)
    I tak dla ostatniego bitu mamy
    1/(2^23) *0 = 1/8388608 * 0 =0 ( to najmniejsza część liczby stało przecinkowej dla mantysy)
    Po zsumowaniu wszystkiego daje :
    M = 1,25 (dec)stało przecinkowo

    Mając już wszystkie składniki liczymy:
    S =0
    M = 1,25
    E = 129
    bias = 127

    X = (-1)^s * M * 2 ^(E-bias)

    X = (-1)^0 * 1,25* (2^(129-127))
    X = 1 * 1,25 * 2^(2)
    X = 1 *1,25 *4 = 5,0000

    Następną liczbą, którą można zapisać będzie z mantysą o jeden większą więc
    M =0x200000 +1 = 0x200001 co w zapisie zmienno przecinkowym bin z początkową jedynką daje
    1,01000000000000000000001 ( bin)
    Przeliczając to na dec mamy (zapisy już tylko dla pozycji na których po przecinku jest 1)
    1/(2^2) *1 = 1/4 *1 = 0,25
    1/(2^23) *1 = 1/8388608 * 1 = 0,00000011920928955078125 ( tyle obliczył calc)
    Co daje w wyniku
    M = 1,25000011920928955078125
    I dalej obliczając jak wyżej da :
    5,000000476837158203125
    A tym samym za pomocą zapisu zmienno przecinkowego pojedynczej precyzji nie da się zapisać szeregu liczb z zakresu od
    5< X < 5,000000476837158203125
    Co więcej drukując liczbę 5,000000476837158203125 z dokładnością do 4 miejsc otrzymamy 5,0000 które nie będzie tym samym 5,0000 z początku postu.
    A jeszcze teraz ja mam kilka pytań ;
    Cytat:

    Do tego implementacja FPU na procesorach AMD, Intel i reszcie może się od siebie nieznacznie różnić

    Możesz zapodać jakiś link ? jak nie to może edytuj post bo forum jest od edukacji a nie od dezinformacji. Sądzisz że kupił bym procesor który byłby niekompatybilny ze standardem ?
    Eagle
  • #21 9830159
    Konto nie istnieje
    Konto nie istnieje  
  • #22 9830244
    Konto nie istnieje
    Konto nie istnieje  
  • #23 9830491
    arnoldziq
    VIP Zasłużony dla elektroda
    Posty: 5376
    Pomógł: 789
    Ocena: 299
    Ta dyskusja nie prowadzi do niczego dobrego.
    Zamykam.

Podsumowanie tematu

✨ Użytkownik poszukiwał rozwiązania dotyczącego pętli w C/C++, która zmniejszałaby wartości zmiennoprzecinkowe z1 o 4 i z2 o 1, aż do momentu, gdy z1 równa się z2, przy założeniu, że z1 > z2. W odpowiedziach poruszono problem reprezentacji liczb zmiennoprzecinkowych oraz dokładności porównań, sugerując użycie progu błędu (d) w warunkach pętli. Zamiast porównania z1 != z2, zalecano użycie warunku (z1 > z2 + d) || (z1 < z2 - d) lub while (fabs(z1 - z2) > 0.001). Dyskusja dotyczyła również różnic w implementacjach FPU na różnych procesorach oraz ich wpływu na wyniki obliczeń. Użytkownik dodał, że jego program ma działać na wielu zmiennych, co wywołało dalsze pytania o optymalizację pętli.
Wygenerowane przez model językowy.
REKLAMA