Autor: | Wojciech Muła |
---|---|
Dodany: | 2002 |
Treść
Średnia arytmetyczna liczona jest według wzoru (nr 1):
av1 = (a+b)/2
W przypadku operacji całkowitoliczbowych dzielenie przez 2 jest równoważne przesunięciu bitowemu w prawo o jedną pozycję.
W takich przypadkach problemem może być przekraczanie zakresu podczas dodawania. Można albo rozszerzyć zakres liczb, albo zastosować troszkę inny wzór — wymaga więcej dzielenia, ale na pewno nie spowoduje przekroczenia zakresu.
Można się przed tym zabezpieczyć, stosując inny wzór (nr 2):
la = LSB(a) lb = LSB(b) av2 = (a/2 + b/2) + (la+lb)/2
Z kolei (la+lb)/2 może zostać zapisane jako (la & lb), co upraszcza nieco sprawę. Można jeszcze bardziej uprościć wzór na av2, pomijając ten czynnik — nie spowoduje to zauważalnych gołym okiem różnic.
; wejście: al, bl ; wyjście: al = (al+bl)/2 : niszczy: ah, bh xor ah, ah xor bh, bh add ax, bx ; a+b shr ax, 1 ; ax/2
Wykorzystano fakt, że wynik dodawania i odejmowanie jest o jeden bit szerszy niż operandy — dodatkowym bitem wyniku jest CF (bit przeniesienia).
; wejście: al, bl ; wyjście: al = (al+bl)/2 add al, bl ; (CF:al) = (0:al) + (0:bl) rcr al, 1 ; podzielenie (CF:al)/2
; wejście: eax, ebx (zawartość każdego rejestru traktowana ; jako wektor 4 bajtów) ; wyjście: eax mov ecx, eax ; ecx = la mov edx, ebx ; edx = lb and ecx, edx ; and ecx, 0x01010101 ; ecx = la & lb ; -- zaznaczona część wystarczy do zaimplementowania ; uproszczonego wzoru na av2 shr eax, 1 ; shr ebx, 1 ; and eax, 0x7f7f7f7f ; a>>1 and ebx, 0x7f7f7f7f ; b>>1 add eax, ebx ; eax = (a>>1 + b>>1) add eax, ecx ; eax += (la&lb)
Niestety, Intel w pierwszej wersji MMX nie wpadł na pomysł rozkazu pavgub.
; wejście: mm0, mm1 - packed bytes ; wyjście: mm3 ; niszczy: mm2, mm3 segment .data mmx_MSB dd 0x80808080, 0x80808080 segment .text movq mm3, [mmx_MSB] movq mm2, mm0 paddb mm0, mm1 ; dodaj "normalnie" paddusb mm2, mm1 ; i z nasyceniem pcmpeqb mm2, mm0 ; tam gdzie wystąpiło nasycenie bajt jest zerowany, ; w przeciwnym przypadku równy 0xff pand mm2, mm3 ; zostaw tylko najstarsze bity — ósme (licząc od zera) ; bity wyniku psrlq mm0, 1 pandn mm3, mm0 ; mm3 = mm0/2 por mm3, mm2 ; uzupełnij wynik ósmym bitem (w wyniku — siódmym)
; wejście: mm0, mm1 = packed bytes ; wyjście: mm0 ; niszczy: mm2, mm3 segment .data mmx_LSB dd 0x01010101,0x01010101 mmx_7lowest dd 0x7f7f7f7f,0x7f7f7f7f segment .text movq mm2, mm0 movq mm3, mm1 psrlq mm0, 1 ; psrlq mm1, 1 ; pand mm0, [mmx_7lowest] ; pand mm1, [mmx_7lowest] ; paddb mm0, mm1 ; mm0 = mm0/2 + mm1/2 pand mm2, mm3 ; pand mm2, [mmx_LSB] ; paddb mm0, mm2 ; mm0 += LSB(mm0) & LSB(mm1)