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] Jak wykonać dodawanie i odejmowanie dużych liczb w C?

koczis83 08 Mar 2009 01:27 3036 9
REKLAMA
  • #1 6251327
    koczis83
    Poziom 10  
    Posty: 5
    Mam taki problem:
    Na wejście podawany jest zestaw składający się z dwóch liczb, mogą to być duże liczby, ale nie mogą składać się z więcej niż 1000 cyfr.
    Na wyjście ma zostać podany wynik odejmowania i dodawania poszczególnych zestawów, wszystko w nowej linii. Np.
    wejście
    100 200
    300 400
    wyjscie
    -100
    300
    -100
    700
    Kod mam taki
    
    #include <stdio.h>
    #include<string.h>
    
    char dodawanie(char liczba1[],char liczba2[])
            {
            char wynik[1001];
            int dlugosc=strlen(liczba1), p=0, w;
           
    
            wynik[dlugosc]=0;
    
                    while ((dlugosc--)>=0)
                    {
    
                    w=(liczba1[dlugosc]-48)+(liczba2[dlugosc]-48)+p;
                            if(w>=10)
                            {
                                    wynik[dlugosc]=(w-10)-48;
                                    p=1;
                            }
    
                            else
                            {
                                    wynik[dlugosc]=w-48;
                                    p=0;
                            }
    
                    }
                  return atof(wynik);
    
            }
           
    char odejmowanie(char liczba1[],char liczba2[])
            {
            char wynik2[1001];
            int dlugosc=strlen(liczba1), q=0, v;
           
    
            wynik2[dlugosc]=0;
    
                    while ((dlugosc--)>=0)
                    {
    
                    v=(liczba1[dlugosc]-48)-(liczba2[dlugosc]-48)-q;
                            if(v < 0)
                            {
                                    wynik2[dlugosc]=(v+10)-48;
                                    q=1;
                            }
    
                            else
                            {
                                    wynik2[dlugosc]=v-48;
                                    q=0;
                            }
    
                    }
    
            return atof(wynik2);
    
            }
           
    void zerowanie(char tab[], int ilosc)
            {
    
            char tabz[1001]="";
    
            while(ilosc--) strcat(tabz,"0");
    
            strcat(tabz,tab);
    
            strcpy(tab,tabz);
    
            }
    
    
    int main () {
    
    char liczba1[1001], liczba2[1001];
    int dlugosc1, dlugosc2, suma, roznica;
    
    while(scanf("%s %s", liczba1, liczba2)==2)
    {
    dlugosc1=strlen(liczba1);
    dlugosc2=strlen(liczba2);
    
            if(dlugosc1<dlugosc2) zerowanie(liczba1,dlugosc2-dlugosc1);
    
            if(dlugosc2<dlugosc1) zerowanie(liczba2,dlugosc1-dlugosc2);
    
            suma=dodawanie(liczba1,liczba2);
            roznica=odejmowanie(liczba1,liczba2);
    printf("%s\n%s\n", roznica, suma);
    
    }
    
    return 0;
    
    } 
    
    


    Niestety nie działa to za dobrze. Domyślam się, że błąd tkwi gdzieś w tych funkcjach z dodawaniem i odejmowaniem. Np użycie przeze mnie atof() nie było chyba dobrym rozwiązaniem, jest to tylko jedna z wielu prób, ale już mi się pomysły skończyły co tam jeszcze można zrobić.

    Jak macie jakiś pomysł to będę wdzięczny.
  • REKLAMA
  • #2 6251386
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Zastanów się, czy liczby wejściowe mogą być ujemne i jak to wtedy będzie działać...

    Twoje funkcje zwracają pojedyńczy znak, a chciałbyś zwrócić tablicę znaków. Niestety w C funkcja nie może zwrócić tablicy. Albo zastosuj zmienną globalną, albo przekazuj tablicę wynikową do funkcji, np tak:
    char* odejmowanie(char odjemna[], char odjemnik[], char roznica[]) {
        /* obliczenia */
        return roznica;
    }

    W ten sposób przekazujesz funkcji informację w którym miejscu w pamięci ma zapisać wynik. Przy instrukcji return nie zwracasz tak naprawdę tablicy, tylko wskaźnik na jej pierwszy element. W ten sposób możesz prosto wykonać coś takiego:
    char wynik[1002], a[1001], b[1001];
    printf("%s\n", odejmowanie(a, b, wynik));

    No i jeszcze jedno - zamiast pisać 48 lepiej napisz '0' - wtedy wiadomo, że chodzi o kod ASCII znaku zero.

    Pozdrawiam,
    Dr.Vee
  • #3 6251900
    koczis83
    Poziom 10  
    Posty: 5
    Poprawiłem zgodzie z Twoimi zaleceniami (mam nadzieję), teraz się kompiluje, ale zamiast uczciwych liczb zwraca krzaczki jakieś, czyli nie działa poprawnie, znowu coś źle przekazuję :(

    Kod teraz wygląda tak:
    
    #include <stdio.h>
    #include<string.h>
    
    char* dodawanie(char liczba1[],char liczba2[], char suma[])
            {
            
            int dlugosc=strlen(liczba1), p=0, w;
           
    
            suma[dlugosc]=0;
    
                    while ((dlugosc--)>=0)
                    {
    
                    w=(liczba1[dlugosc]-'0')+(liczba2[dlugosc]-'0')+p;
                            if(w>=10)
                            {
                                    suma[dlugosc]=(w-10)-'0';
                                    p=1;
                            }
    
                            else
                            {
                                    suma[dlugosc]=w-'0';
                                    p=0;
                            }
    
                    }
                  return suma;
    
            }
           
    char* odejmowanie(char liczba1[],char liczba2[], char roznica[])
            {
            
            int dlugosc=strlen(liczba1), q=0, v;
           
    
            roznica[dlugosc]=0;
    
                    while ((dlugosc--)>=0)
                    {
    
                    v=(liczba1[dlugosc]-'0')-(liczba2[dlugosc]-'0')-q;
                            if(v < 0)
                            {
                                    roznica[dlugosc]=(v+10)-'0';
                                    q=1;
                            }
    
                            else
                            {
                                    roznica[dlugosc]=v-'0';
                                    q=0;
                            }
    
                    }
    
            return roznica;
    
            }
           
    void zerowanie(char tab[], int ilosc)
            {
    
            char tabz[1001]="";
    
            while(ilosc--) strcat(tabz,"0");
    
            strcat(tabz,tab);
    
            strcpy(tab,tabz);
    
            }
    
    
    int main () {
    
    char liczba1[1001], liczba2[1001], wynik[1002], wynik2[1002];
    int dlugosc1, dlugosc2;
    
    while(scanf("%s %s", liczba1, liczba2)==2)
    {
    dlugosc1=strlen(liczba1);
    dlugosc2=strlen(liczba2);
    
            if(dlugosc1<dlugosc2) zerowanie(liczba1,dlugosc2-dlugosc1);
    
            if(dlugosc2<dlugosc1) zerowanie(liczba2,dlugosc1-dlugosc2);
    
           
    printf("%s\n%s\n", odejmowanie(liczba1,liczba2,wynik), dodawanie(liczba1,liczba2,wynik2));
    
    }
    
    return 0;
    
    } 
    
    


    Zauważyłem, że odejmowanie może nie działać prawidłowo, jeśli pierwsza liczba ma na końcu kilka zer np 1000 - 755, zamiast 255 daje zdaję się 355, no ale to raczej nie ma wpływu na pojawianie się tych krzaków.

    Dalej nie wiem o co w tym chodzi :(, ale dziękuję za zainteresowanie :)
  • REKLAMA
  • #4 6252221
    BoskiDialer
    Poziom 34  
    Posty: 1530
    Pomógł: 353
    Ocena: 42
    Krzaki? przy odejmowaniu zamiana ascii->dec rozumiem odejmowanie wartości znaku '0', ale przy konwersji dec->ascii raczej powinieneś dodawać wartość znaku '0' żeby uzyskać poprawny znak.
  • #5 6252250
    koczis83
    Poziom 10  
    Posty: 5
    no ba jasne, że tak :D, kurcze już bym to wczoraj skończył, nie połapałem się :D Dzięki wielkie :)

    Dodano po 1 [godziny] 42 [minuty]:

    Teraz już prawie działa, natomiast problem dotyczy samych działań. Faktycznie jak dodam np 99+1 dostanę 00 zamiast 100, odejmowanie natomiast działa np 100 -1 daje 099 zamiast 99, natomiast odejmowanie 100-300 daje 800.

    Z tego co zrozumiałem to wynik dla dodawania w takim przypadku powinien mieć długość większą od długości liczby o 1 i w wynik[0] powinien wówczas przechowywać wartość 1, ale nie wiem za bardzo jak to zrobić, bo jak zwiększyłem długość wyniku o 1 tzn wynik[dlugosc+1] i ustawiłem, dla warunku w>=10 taki warunek:
    if(wynik[1]) wynik[0]=1+'0';


    to wówczas 99 +1 dało mi 100, ale np 100+1 dało wynik niepoprawny krzaczek i dwa zera. Oczywiście wszędzie zmieniła wynik[dlugosc] na wynik[dlugosc+1]

    Z odejmowaniem to w ogóle nie mam pojęcia jak uciąć to zera i jak rozwiązać problem ujemnego wyniku.
  • REKLAMA
  • #6 6252930
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Po wyjściu z pętli sprawdzaj wartość przeniesienia. Jeśli jest niezerowe, to wykonaj operację na kolejnej cyfrze.

    Z arytmetyki wiadomo, że przy maksymalnym przeniesieniu wejściowym (z poprzedniej cyfry) = 1 maksymalne przeniesienie wyjściowe również = 1, bo 1+9+9 < 20.

    Zastanów się jeszcze co się stanie, gdy od liczby 1 odejmiesz 20...

    Pozdrawiam,
    Dr.Vee
  • #7 6254219
    koczis83
    Poziom 10  
    Posty: 5
    No to jeśli dobrze zrozumiałem, a obawiam się, że nie, mam sprawdzić jaki stan przyjmuje to moje p po wyjściu z pętli, jeśli jest p=1 to wtedy muszę jeszcze dorzucić tą jedynkę na początek, bo to zawsze będzie 1. Tylko, że jak ja mam to zrobić skoro ta ostatnia pozycja przypada na suma[0], nie mogę tego dać przecież na suma[-1].
  • REKLAMA
  • #8 6254375
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Chyba cały czas zakładasz, że liczby mają równą długość.

    Dzięki temu, że zwracasz z funkcji wskaźnik możesz sobie ułatwić - np. ostatnią (najmniej znaczącą) cyfrę wstawiasz na sam koniec tablicy wynikowej, kolejne wstawiasz przesuwając się ku początkowi. Na koniec dodajesz ew. ekstra cyfrę, ew. minus, i zwracasz wskaźnik na ostatni wstawiony znak...

    PS. Mała rada - zrób sobie automatyczny test na początku programu, np:
    #include <assert.h>
    
    int main(void) {
        char wynik[1001+2];
        assert(strcmp(dodawanie("1", "99", wynik), "100") == 0);
        /* itd */
    }


    Pozdrawiam,
    Dr.Vee
  • #9 6255697
    koczis83
    Poziom 10  
    Posty: 5
    Cytat:
    Dzięki temu, że zwracasz z funkcji wskaźnik możesz sobie ułatwić - np. ostatnią (najmniej znaczącą) cyfrę wstawiasz na sam koniec tablicy wynikowej, kolejne wstawiasz przesuwając się ku początkowi. Na koniec dodajesz ew. ekstra cyfrę, ew. minus, i zwracasz wskaźnik na ostatni wstawiony znak...


    No ale właśnie tego nie mogę pojąć. Jak mam kod taki jak teraz, to dla mnie to dodawanie czy odejmowanie jest jasne, zresztą robiłem to to takie być musi. Dodanie znaku czy dodatkowej 1 na początku tabeli jest już dla mnie abstrakcją, jakoś nie mogę sobie tego wyobrazić przez co nic mi nie wychodzi :(
    Nie wiem co oznacza najmniej znacząca cyfra - przecież wszystkie są istotne dla wyniku chyba... Jakoś nie mogę tego pojąć...
  • #10 6256189
    Dr.Vee
    VIP Zasłużony dla elektroda
    Posty: 1784
    Pomógł: 307
    Ocena: 76
    Najmniej znacząca = z najmniejszą wagą. Np. w liczbie 123 cyfra 3 jest najmniej znaczącą. Jak dodajesz liczby na kartce to zaczynasz od najmniej znaczącej.

    Przykład ze zwracaniem wskaźnika:
    #include <string.h>
    #include <stdio.h>
    
    #define ROZMIAR 20
    
    char* funkcja(char wynik[])
    {
        const char* przyklad = "1234";
        unsigned dlugosc = strlen(przyklad);
        unsigned i;
    
        /* p wskazuje na kolejny wolny znak */
        char* p = &wynik[ROZMIAR-1];
    
        /* najpierw kończące zero */
        *p-- = '\0';
    
        /* skopiuj przykład od końca */
        for (i = 1; i <= dlugosc; ++i)
            *p-- = przyklad[dlugosc-i];
    
        /* dodajemy minus */
        *p-- = '-';
    
        /* zwracasz adres ostatniego zapisanego znaku */
        return p + 1;
    }
    
    int main(void)
    {
        char wynik[ROZMIAR];
        printf("wynik = %s\n", funkcja(wynik));
        return 0;
    }
    


    Pozdrawiam,
    Dr.Vee

Podsumowanie tematu

✨ Dyskusja dotyczy implementacji dodawania i odejmowania dużych liczb (do 1000 cyfr) w języku C. Problemem jest prawidłowe zarządzanie wynikami operacji arytmetycznych na liczbach reprezentowanych jako łańcuchy znaków. Wskazano, że funkcje w C nie mogą zwracać tablic znaków bezpośrednio, dlatego należy przekazywać tablicę wynikową jako argument funkcji i zwracać wskaźnik do niej. Podkreślono konieczność poprawnej konwersji znaków ASCII na wartości liczbowe i odwrotnie (odejmowanie i dodawanie znaku '0'). Omówiono obsługę przeniesienia (carry) w dodawaniu, zwłaszcza gdy wynik ma długość większą o 1 niż liczby wejściowe, oraz problem ujemnych wyników w odejmowaniu. Zaproponowano przechowywanie cyfr od najmniej znaczącej (końca tablicy) ku najbardziej znaczącej (początkowi), co ułatwia dodawanie dodatkowej cyfry przeniesienia lub znaku minus. Przykład pokazuje, jak zwracać wskaźnik do początku wyniku w tablicy, umożliwiając poprawne wyświetlanie wyniku. Zasugerowano także stosowanie testów automatycznych do weryfikacji poprawności funkcji.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA