| Subject | FixedMath |
| pOiSOn | |
| Irc | irc.ada.net.tr #scene.tr #manowar #atheist |
| ICQ | 6795916 |
| Last Update | 1 Ocak 2001 [ off bea.. amma zaman geçti..] |
| Note | fpu sucks.. |
typedef long Fixed32;
artık Fixed32 tipinde bir sayı tipimiz var.. bizim kullanacağımız 16.16'lik fixed olduğuna göre normal sayılardan fixed tipe çevrim için gerekli makroları düzenleyelim..
#define INT_TO_FIXED(x) ((x) << 16)
#define DOUBLE_TO_FIXED(x) ((long)(x * 65536.0 + 0.5))
#define FIXED_TO_INT(x) ((x) >> 16)
#define FIXED_TO_DOUBLE(x) (((double)(x)) / 65536.0)
#define ROUND_FIXED_TO_INT(x) (((x) + 0x8000) >> 16)
#define DTF DOUBLE_TO_FIXED
#define FTD FIXED_TO_DOUBLE
#define ITF INT_TO_FIXED
#define FTI FIXED_TO_INT
#define RTI ROUND_FIXED_TO_INT
#define ONE INT_TO_FIXED(1)
#define FIXED_PI 205887L
#define FIXED_2PI 411775L
#define FIXED_E 178144L
#define FIXED_ROOT2 74804L
#define FIXED_ROOT3 113512L
#define FIXED_GOLDEN 106039L
kodları incelediğinizde olayın büyük bir kısmını kapmış olacaksınız.. örneğin int tipinden fixed'e çevrim için sayı 16 bit sola kaydırılıyor böylece integer sayi'yi high 16 bitlere getirmiş oluyoruz, noktadan sonraki sayi ise 0 olacaktır yani float yazımında n.0 sayısını elde etmiş olduk..
aynı şekilde fixed sayıdan int'e çevrim için sayıyı 16bit sağa kaydırmamız yeterli.. double sayılar için neden 65536 ile çarpıp 0.5 ekleniyor derseniz 14.7 sayısı için işlemi yapın nedeni anlaşılacaktır..
ROUNT_TO_FIXED die bi makro var bu makro ile 14.7 sayısını 15 olarak integer'a çevirmeniz için kullanılabilir..
makro devamlarında uzun uzun makro adları yerine kısaltmaları eklenmiş.. böylece INT_TO_FIXED yerine ITF kullanabilirsiniz..
biraz alta da gerekebilecek sabit sayılar var.. lazım olur belki :)..
dont panic! :).. 4 işlemin ikisi yani toplama ve çıkartma normal yolla yani integer'larla nasi yapıyorsaniz aynı şekilde yapabilirsiniz.. sadece bölme ve çarpma işlemleri icin iki tane makro (compiler watcom değilse asm fonks olarak yazmanız gerekecek..) kullanmanız gerekli..
Fixed32 FixedMul(Fixed32 num1, Fixed32 num2);
Fixed32 FixedDiv(Fixed32 numer, Fixed32 denom);
#pragma aux FixedMul = \
"imul edx" \
"add eax, 8000h" \
"adc edx, 0" \
"shrd eax, edx, 16" \
parm caller [eax] [edx] \
value [eax] \
modify [eax edx];
#pragma aux FixedDiv = \
"xor eax, eax" \
"shrd eax, edx, 16" \
"sar edx, 16" \
"idiv ebx" \
parm caller [edx] [ebx] \
value [eax] \
modify [eax ebx edx];
Fixed32 FixedSquare(Fixed32 n);
Fixed32 OneOver(Fixed32 n);
Fixed32 FixedSqrtLP(Fixed32 n); // Low Precision (8.8)
Fixed32 FixedSqrtHP(Fixed32 n); // High Precision (8.16)
// This is faster than using FixedMul for squares.
#pragma aux FixedSquare = \
"imul eax" \
"add eax, 8000h" \
"adc edx, 0" \
"shrd eax, edx, 16" \
parm caller [eax] \
value [eax] \
modify [eax edx];
// This is faster than using FixedDiv.
#pragma aux OneOver = \
"xor eax, eax" \
"mov edx, 1" \
"idiv ebx" \
parm caller [ebx] \
value [eax] \
modify [eax ebx edx];
#pragma aux FixedSqrtLP = \
" xor eax, eax" \
" mov ebx, 40000000h" \
"sqrtLP1: mov edx, ecx" \
" sub edx, ebx" \
" jl sqrtLP2" \
" sub edx, eax" \
" jl sqrtLP2" \
" mov ecx,edx" \
" shr eax, 1" \
" or eax, ebx" \
" shr ebx, 2" \
" jnz sqrtLP1" \
" shl eax, 8" \
" jmp sqrtLP3" \
"sqrtLP2: shr eax, 1" \
" shr ebx, 2" \
" jnz sqrtLP1" \
" shl eax, 8" \
"sqrtLP3: nop" \
parm caller [ecx] \
value [eax] \
modify [eax ebx ecx edx];
#pragma aux FixedSqrtHP = \
" xor eax, eax" \
" mov ebx, 40000000h" \
"sqrtHP1: mov edx, ecx" \
" sub edx, ebx" \
" jb sqrtHP2" \
" sub edx, eax" \
" jb sqrtHP2" \
" mov ecx,edx" \
" shr eax, 1" \
" or eax, ebx" \
" shr ebx, 2" \
" jnz sqrtHP1" \
" jz sqrtHP5" \
"sqrtHP2: shr eax, 1" \
" shr ebx, 2" \
" jnz sqrtHP1" \
"sqrtHP5: mov ebx, 00004000h" \
" shl eax, 16" \
" shl ecx, 16" \
"sqrtHP3: mov edx, ecx" \
" sub edx, ebx" \
" jb sqrtHP4" \
" sub edx, eax" \
" jb sqrtHP4" \
" mov ecx, edx" \
" shr eax, 1" \
" or eax, ebx" \
" shr ebx, 2" \
" jnz sqrtHP3" \
" jmp sqrtHP6" \
"sqrtHP4: shr eax, 1" \
" shr ebx, 2" \
" jnz sqrtHP3" \
"sqrtHP6: nop" \
parm caller [ecx] \
value [eax] \
modify [eax ebx ecx edx];
açılarda da fixed math kullanabilirsiniz..bunun için kendi açı tipimizi tanımlıyoruz..
typedef unsigned short Iangle; // Integer angle (0..1023)
bu açı tipi bildiğimizden biraz farklı normalde 360' lık bir açı alanı kullanırız.. fakat açıyı bulmak için bu sefer matematik işlemleri yerine bir lookup dizisi kullanıcaz.. bu halde de 360' lik bir lookup bazı işlemler için biraz kaba gelebilir bunun yerine 1024' lük bir sistem kullanıcaz.. buna göre 0' = 0 , 360' = 1024 oranında bir lookup kullanılıyor.. bir çok işte 30' gibi spesifik açılarla uraşmadığımızdan (yani açıyı sadece arttırıp 360'ı bulunca sıfırlamaktan başka bişey yapmıyoruz..) böylesi daha iyi üstelik.. açı 1024'ü geçtiğinde (sayı tipinden dolayı) kendiğilinden tekrar 0 olacaktır..
gerekli lookup tanımlamaları..
#define MAX_TRIG 1024
extern Fixed32 CosTab[MAX_TRIG];
extern Fixed32 SinTab[MAX_TRIG];
#include "sintab.hpp"
#include "costab.hpp"
sintab.hpp ve costab.hpp dosyalarını sadece bir lookup dizisi içerdiğinden buraya eklemedim, ekteki zip içerisinde hem bu dosyaları, hemde bu dosyaları yaratan ufak bi utility'i bulcaksınız.. source kod olduğu için gerektiğinde kendinize göre değiştirebilirsiniz..
ayrıca gerektiğinde (lookupdan direkt getirmek yerine) veya tanjant gibi açılara ihtiyaç duyduğunuzda aşağıdaki fonks. var..
void CosSin(Iangle theta, Fixed32 *Cos, Fixed32 *Sin)
{
theta &= (MAX_TRIG - 1);
*Sin = SinTab[theta];
*Cos = CosTab[theta];
}
Fixed32 Tan(Iangle theta)
{
// This shifting stuff is for better accuracy.
theta &= (MAX_TRIG - 1);
return (FixedDiv(SinTab[theta] << 16, CosTab[theta]) >> 16);
}
iyi şans..