Autor Wątek: Organizacja klas dla typów liczbowych  (Przeczytany 2316 razy)

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 14:13:49
witajcie, piszę klasy (biblioteki) do obliczeń dowolnej precyzji opartych o stringi. Wymyśliłem sobie to tak, że będą 2 klasy -> 1 do obliczeń na typach całkowitych, druga zaś dla liczb rzeczywistych. Klasa do obliczeń na typach całkowitych już jest, odpowiednie algorytmy są już w niej zaimplementowane i teraz przenoszę się do pisania SReal czyli obliczania liczb rzeczywistych i tu pojawia się problem. Chciałbym wymusić możliwie najmniejszą redundancje kodu, algorytmy są właściwie takie same przy obliczaniu liczb całkowitych i rzeczywistych i zastanawiam się czy nie powinienem przenieść tych algorytmów operowania na stringach do osobnej klasy tak żebym mógł ich użyć w klasach SReal i SInt. Tu jednak pojawiają się problemy natury 'jak to poprawnie zrobić' bo zrobić jakkolwiek bardzo łatwo.

Takie sobie wstępnie przyjąłem założenia:
-Nie chciałbym mieć "wspólnego mianownika" obu klas, tzn nie chcę dawać możliwości mieszania klas przez związki dziedziczenia np. bazowa* bb = new SInt(....) | new SReal(....)
-zachowanie klas dokładnie takie jak typów wbudowanych czyli operatory, możliwość rzutowania itd.. (jednym słowem łatwe użytkowanie)
 
nieco bardziej obrazowo:

SInt (algorytmy do operowania na liczbach całkowitych [+,-,*,/] )
SReal ( powinny być algorytmy do operowania na liczbach rzeczywistych: problemem jest to, że tak na prawdę musiałbym skopiować algorytmy do operowania na liczbach całkowitych bo to będzie właściwie to samo tylko w trochę inny sposób użyte ]

Jak to zrobić bez niepotrzebnego ctr+c, ctr+v. Czy wydzielenie osobnej klasy dla tych algorytmów to dobry pomysł? dziedziczenie mi się tu nie podoba, może agregacja? w sumie nie wiem czy wydzielenie osobnej klasy to dobry pomysł. Często tak robię (wydzielam do osobnych klas) ale tu mi jakoś to nie pasuje... może ktoś podpowie jak to ładnie zaprojektować :)

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline izaw

  • Użytkownik

# Kwiecień 29, 2012, 15:00:06
Oj coś chyba nie tak.

Jak implementujesz liczby rzeczywiste, Jako stało- czy zmienno-przecinkowe?

Jeżeli stałoprzecinkowe to możesz taką liczbę pamiętać jako dwie liczby SInt dla części całkowitej i ułamkowej lub jedna liczba SInt plus pozycja przecinka. W zależności od wyboru łatwiejsze jest dodawanie/odejmowanie lub mnożenie/dzielenie.

Jeżeli jako zmiennoprzecinkowe to mantysa jako SInt i wykładnik jako int (wystarczy). Tutaj też mnożenie/dzielenie jest prostsze.

I nie są te podobne algorytmy, tylko wykorzystanie algorytmów dla obliczeń na składowych, czyli liczbach typu liczbach SInt.

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 15:23:21
izaw, jako stałoprzecinkowe, właśnie tak chciałem to zrobić jak napisałaś, wtedy część ułamkowa zwraca przeniesienie i część całkowita uwzględnia to przeniesienie. Tylko ja nie myślałem o tym, żeby wykorzystać do tego SInta ^^ ( a właściwie to myślałem ale z jakiegoś powodu wydało mi się to złe )

Offline izaw

  • Użytkownik

# Kwiecień 29, 2012, 17:43:16
Po co pisać kod wielokrotnie. Należy wykorzystywać inne klasy.

Inaczej można próbować na szablonach, ale pogubisz się.

Offline hashedone

  • Użytkownik

# Kwiecień 29, 2012, 19:14:32
Ale to zaraz - jak przechowujesz te liczby? Jako stringi, czy jako stałoprzecinkowe? Bo raz mówisz że tak, a raz że tak...

Offline MrKaktus

  • Użytkownik

# Kwiecień 29, 2012, 19:21:38
Chwila chwila , ale mowiac stringi chyba nie masz na mysli "123.214324234322334..." tylko ciagi bajtow o dlugosci odpowiadajacej wymaganej precyzji bitowej?

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 21:08:48
miałem na myśli "121.3123123":)

Offline izaw

  • Użytkownik

# Kwiecień 29, 2012, 21:32:15
Uściślij. Stringi tylko jako interfejs we/wy, czy też same liczby wewnętrznie przechowywane jako stringi.

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 22:03:48
liczby wewnętrznie przechowywane jako stringi (mam zdefiniowane działania na takich stringach[+,-,*,/]):

wpisuję: "12111.123131" + "1232134213.5322341" i zwraca mi to "..." wynik w postaci stringu (działania na "znakach". Nie działam na intach tylko stale na stringach (oczywiście wewnątrz interpretując string jako char (0-9) i odpowiednio działając przy przeniesieniach :)
« Ostatnia zmiana: Kwiecień 29, 2012, 22:06:37 wysłana przez Puchaczov »

Offline izaw

  • Użytkownik

# Kwiecień 29, 2012, 22:31:14
Strasznie nieoptymalne. Masz liczbę jako int i... nic o niej nie wiesz.

Dodatkowo przeniesienia/pożyczki z działań na cyfr ach jako znakach. Makabra.

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 22:48:06
jak nic o niej nie wiem, przecież jak mam np. "123" + "456" to wiem, że ascii jest to [49, 50, 51] + [52, 53, 54]
jeżeli (49-48)+(52-48) z tego już wiem czy >9 jeżeli tak to mam przeniesienie i uwzględniam je w następnym obiegu pętli. Dodatkowo zapisuję pozycje przecinka i rozdzielam całość na to "za przecinkiem" i "przed przecinkiem"

        for( ;i>=0 && j>=0; --i, --j)
        {
            partialResult = ( sumComponent1.number[i] + sumComponent2.number[j] ) - 96 + transfer;
            if(partialResult > 9)
            {
                result.number.insert(result.number.begin(),(char)(partialResult+38));
                transfer=1;
            }
            else
            {
                result.number.insert(result.number.begin(), (char)partialResult+48);
                transfer=0;
            }
            ++result.dotPos;
        }
 

wstawianie będzie tutaj powolne pewnie ale chwilowo mało mnie to interesuje :)
« Ostatnia zmiana: Kwiecień 29, 2012, 22:57:39 wysłana przez Puchaczov »

Offline izaw

  • Użytkownik

# Kwiecień 29, 2012, 23:09:05
Chwilowo uczysz się złych nawyków, a potem ci to zostanie.

Nie stosuj liczb magicznych 48, 96 itp.
Ten sposób przechowywania powodujesz, że ciągle kodujesz i dekodujesz znaki na odpowiadające im wartości.
Milcząco zakładasz, że masz kodowanie ASCII. A jeżeli implementacja stringa okaże się inna?

Offline Puchaczov

  • Użytkownik

# Kwiecień 29, 2012, 23:10:44
to nie wiedziałem, jak inaczej powinienem to zrobić?

mógłbym to zrobić tak, że utworzyłbym zmienną int x = (int)(std::string("0"))[0] i zamiast 48, 96 miałbym x, 2x
« Ostatnia zmiana: Kwiecień 29, 2012, 23:29:58 wysłana przez Puchaczov »

Offline izaw

  • Użytkownik

# Kwiecień 30, 2012, 00:21:27
Coś takiego:
const int zero = '0';
int cyfra = liczba[0] - zero;
Lepiej wartość liczby przechowywać w wektorze liczb całkowitych. I nie wartość odpowiadającą jednej cyfrze a kilku. Np. 9. Zakres liczb to [0, 10^n), gdzie n to ilość cyfr kodowanych w jednej zmiennej.

Oczywiści to wszystko w celu ćwiczenia. Do celów kodowania lepiej uzyć gotowych bibliotek.

Offline hashedone

  • Użytkownik

# Kwiecień 30, 2012, 09:17:21
Przede wszystkim musisz poczytać o tym, jak się liczby trzyma w pamięci. Do pogooglania: kod binarny, U2, liczby zmiennoprzecinkowe, liczby stałoprzecinkowe, standard IEEE.