Oszacowanie log2

Autor: Wojciech Muła
Dodany:10.02.2002
Aktualizacja:14.12.2003

Część całkowita log2

Wartość liczby zmiennoprzecinkowej jest liczona wg wzoru:

x = − 1S ⋅ (1 + mantissa) ⋅ 2exponent + bias

Część całkowita log2(x) z tej liczby to dokładnie exponentbias.

; eax - liczba o pojedynczej precyzji (float)

and eax, 0x7fffffff ; zerowanie bitu znaku
shr eax, 23         ; |0000 0000|0000 0000|0000 0000|exp+bias |
sub  al, 127        ; |0000 0000|0000 0000|0000 0000|exponent |

To powinno wystarczyć do większości zastosowań i jak widać kod jest bardzo prosty.

Aproksymacja log2

Znormalizowana mantysa należy do przedziału [1..2) (dokładnie [1..8388607/8388608)). Poniżej przedstawiono wykresy funkcji x − 1 (kolor czerwony) i log2(x) (kolor niebieski), dla xin[1..2).

wykres funkcji log2x i x-1 dla x=1..2

Jak widać w tym zakresie funkcja log2(x) jest prawie liniowa, można więc aproksymować ją wartością mantissa. Ze wzoru można obliczyć wartość 1.0 + mantissa, gdy zostanie wyzerowany wykładnik:

; eax - liczba o pojedynczej precyzji (float)

bias equ 127

and eax, 0x7fffff       ; wyzeruj wykładnik i bit znaku
or  eax, (0+bias) << 23 ; ustaw nowy wykładnik (0x3f800000)

Maksymalna różnica (błąd bezwzględny) pomiędzy log2(x) − (x − 1) w przedziale [1, 2] to mniej więcej 0.086. Błąd bezwzględny można zmniejszyć o połowę, gdy użyta zostanie prosta o równaniu x − 0.957.

segment .data

temp dd 0              ; bufor dla operacji FPU
p    dd -128.0 + 0.043 ; poprawka

segment .text
; eax - liczba o pojedynczej precyzji (float)

and eax, 0x7fffff       ; wyzeruj wykładnik i bit znaku
mov ebx, eax

shr eax, 23             ;
sub  al, 127            ; eax = exponent

and ebx, 0x7fffff       ;
or  ebx, 0x3f8000       ; ebx = 1.0+mantissa

mov   [temp], eax
fistp [temp]            ; st0  = exponent

mov   [temp], ebx
fadd  [temp]            ; st0 += 1.0+mantissa
fadd  [p]               ; st0 += p

fstp  [temp]            ; temp = st0