całkowitych a dokładność

Sep 19, 2012 10 Replies

Witam,



Trochę już minęło czasu od zajęć z metod numerycznych... więc jak najlepiej wykonać taką operację:



Mam liczbę całkowitą która maksymalnie osiąga wartość 2^13, ale muszę ją pomnożyć przez niecałkowity współczynnik, który będzie wynosił około 0,3. Zmienne wykorzystywane w obliczeniach są 16 bitowe (nie mam tyle mocy obliczeniowej żeby przejść na liczby zmiennoprzecinkowe). Ponieważ 0,3 to to samo co 3/10, to mogę sobie rozbić moje działanie na mnożenie przez 3 i dzielenie bez reszty przez 10.



Pytanie - jaka kolejność zapewnia większą dokładność? Najpierw mnożyć czy lepiej najpierw dzielić? Co w przypadku gdy mój współczynnik wyniesie np.



0,32 - wykonanie jako pierwszego mnożenia przez 32 (a potem dzielenia przez
100) da mi pośredni wynik większy niż zakres mojej 16-bitowej zmiennej.

Jakub Rakus snipped-for-privacy@op.pl napisał(a):

Najpierw mnożyć, żebyś błędu zaokrąglenia nie mnożył.

Mnóż przez 8 a potem dziel przez 25.

Dnia Wed, 19 Sep 2012 19:25:51 +0200, od Jakub Rakus snipped-for-privacy@op.pl nadeszla wiadomosc:

0,32 = 3/10 + 2/100

Nie musisz mnożyć przez więcej niż 9 ;-)

Faktycznie, wykonałem wcześniej kilka obliczeń "na kartce" na losowo wymyślonych liczbach i tak też mi się wydawało.

Też nad tym myślałem, tylko jeszcze nie bardzo wiem jak programowo rozwiązać zamianę liczby z zakresu 0.20-0.50 na najbliższy jej ułamek zwykły o liczniku nie większym od 8. Mój współczynnik będzie najpierw ustawiany podczas procesu automatycznej kalibracji - całość bowiem dotyczy przeliczenia wartości uzyskanej z przetwornika A/C.

Użytkownik "Jakub Rakus" napisał w wiadomości grup

Chyba nie masz watpliwosci, ze np 4/10 = 0, 0*3= 0 ?

O to to to :-)

Jesli masz dostep do assemblera/ukladu mnozacego, to nie mnozysz przez

0.32, ale np przez 20972. Tylko ze robisz mnozenie z wynikiem 32-bit, z ktorego wybierasz dalej tylko starsze slowo. Bo 65536*0.32=20971.52

jesli te wspolczynniki sa tego rzedu, to mozesz rozlozyc

0.32=0.0101001...b a wiec x*0.32 = x>>2 + x>>4 + x>>7 ...

J.

W dniu 2012-09-19 19:25, Jakub Rakus pisze:

Nie napisałeś w jakim języku chcesz pisać. Jeśli w c to użyj double. Jeśli w asm to musisz na potrzeby tego mnożenia napisać własną procedurkę mnożenia dwóch 16 bitowych z wynikiem na np 24 czy 32 bitach. Kolejność działań to oczywiście najpierw mnożenie potem dzielenie.

Am 19.09.2012 22:32, schrieb Mario:

Przecież pisze wyraźnie, że nie ma mocy obliczeniowej na floaty, a ty mu z double wyskakujesz.

Osobiście robię takie rzeczy tak, by jak najmniej stracić i szybko liczyć. Czyli ułamek wyrazić jako ułamek dwójkowy, czyli na przykład twoje 0.32 jako 41/128. Następnie mnożę dwie liczby 16to bitowe z wynikiem 32 bity i przesuwam o n = log2(128) czyli 7 w prawo. Na większości procesorów działa bez problemu. W przypadku bardziej "krzywych" współczynników rozkładam na pasujące czynniki, które są później sumowane. Jak procek ma jednostkę mnożącą, to nie trzeba przesuwać i można od razu mnożyć, ale mianownik dwójkowy jest zawsze wygodniejszy.

Waldek

W dniu 2012-09-20 09:49, Waldemar Krzok pisze:

Sorki miało być long.

Zdaje się, że pytający właśnie nie wie jak zrobić żeby mieć wynik 32 bitowy.

Będę marudził, ale nie chce liczb 32-bitowych :P więc to odpada.

Ale za to to rozwiązanie mi się podoba :) Przeliczenie współczynnika na liczbę 8 czy nawet 16-bitową i tak będzie wykonane tylko raz na jakiś czas podczas procedury kalibracji, a trochę przesunięć bitowych nie zeżre mi dużo cennego czasu i kodu programu.

Masz 8 bitowy procesor? To nadal mnożenie 2 16 bitowych w 32 bitową to tylko 4 mnożenia. A przesuwanie bitowe liczb dłuzszych niż rejestr też trochę kosztuje.

W złym wypadku masz 12 tych działań.

Trochę teraz pogdybam. Jeśli jednak masz procek 16 bitowy, możesz pomysł J.F. nieco pożyłować. x ma być 12 bitowe (mówiłeś, że maks to 2^13. Zakładam, że ograniczenie się do 2^13-1 nie jest problemem) Mamy więc całe 3 bity, możemy mnożyć przez 111_b i będzie ok.

(x*a_1)>>3 + (x*a_2)>>6 + (x*a_3)>>9 + (x*a_4)>>12 + (x*a_5)>>15

gdzie a_i to kolejne trójki bitów Twojego mnożnika.

Jeśli mnożnik jest w okolicach 0.3, dokładniej jest <0.5 to od razu (x*a_1)>>4 + (x*a_2)>>7 + (x*a_3)>>10 + (x*a_4)>>13 + (x*a_5)>>16

Czyli samo (x*a_1)>>4 + (x*a_2)>>7 + (x*a_3)>>10 + (x*a_4)>>13 bo (x*a_5) jest 16 bitowe, więc przesunięcie go zjada.

pzdr bartekltg

Jak nie zmiennoprzecinkowe, to może stałoprzecinkowe? O ile dobrze zrozumiałem z innych postów, to kalibracja może być złożona obliczeniowo, natomiast aplikowanie współczynnika ma być szybkie, proste i dokładne. Ja bym to zrobił tak: dany współczynnik (zakładając, że jest mniejszy od

1) zapisał jako liczbę stałoprzecinkową np. 0.16 (tzn 0 bitów liczby całkowitej i 16 bitów po przecinku) lub jak nie jest potrzebna aż taka dokładność, to 0.8. Potem w zasadzie tylko mnożysz daną przez ten współczynnik (mnożenie 16bitx16bit lub 16bitx8bit), wywalasz wszystko 'po przecinku' do kosza (czyli bierzesz za wynik 2 najstarsze bajty) i tyle. Jeżeli masz w procku hardwarowe mnożenie, to będzie bardzo szybko. Natomiast przy kalibracji możesz się trochę pogimnastykować, żeby wyliczyć ten współczynnik (też na stałoprzecinkowych, pamiętając o zakresach liczb).

Podstawowym błędem w rozumowaniu jest przyjęcie, że współczynnik bedziesz trzymał w postaci dziesiętnej.

Pozdrawiam

Marek

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required