Hepimiz C ve C dilinde math.h kütüphanesini bir kere de olsa kullanmışızdır. Hepimizin bildiği üzere içinde bulunan sin cos ve sqrt gibi işlemler işimizi oldukça kolaylaştırmakta. Peki bu kod yapısı örneğin 3'ün karekökünü bana nasıl veriyor? Ya da bir sayının sin cos'unu nasıl hesaplıyor. İçeriğindeki mantık nedir?
Numerik Hesaplama Yöntemleri kullanılıyor.
Misal karekök için birçok yöntem varmış...
http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
Araştırdıkça konu hoşuma gitmeye başladı.
Özellikle Newton matematik için ciddi gelişimler yapmış. Bkn. Türev ve İntegral :)
Tamsayı Karekök Algoritması:
http://www.embedded-systems.com/98/9802fe2.htm
Diğerlerini bulan arkadaşların çözümlerini de bekleriz.
Trigonometrik fonksiyonlar için tablo kullanılıyor olabilir. Bunları ekleyince program hafızası kullanımında ciddi bir artış gözleniyor. Emin değilim ama.
Ayrıca, fonksiyonları kullanmak bir kenara, floating point ile işlem yapmak bile ayrı bir mesele. Floating point ile dört işlem yapılırken bile arka planda bir dolu iş yapılıyor. C18'de bu alt işlemlerin her birine karşılık gelen ayrı asm dosyaları var.
Fonksiyonlarla ilgili kodların bir kısmını C18'in klasörü içinde bulmak mümkün ama benim bulduklarım sistemin nasıl çalıştığı hakkında tam bir bilgi vermiyor. Belki de ben görememişimdir.
Trigonometrik ifadelerin de açılımı var,
Gördüğüm kadarı ile gayet kolay hesaplabiliyor.
Float sayı ile hesabı gerektiğinden uzun süren işlemler olabilir.
http://www.pdfill.com/example/Numerical_Methods.pdf
aradığınız cevap Taylor serisi olabilirmi?
http://dotancohen.com/eng/taylor-sine.php
Hesaplayicilarda sikca kullanilan algoritma Cordic algoritmasidir.
Bu konuyu araştırırken çok ilginç bir kod buldum. Yöntem aslında 1990'larda geliştirilmiş. 1999 yılında Quake III oyun motorunda kullanılmış, 2002,2003 yıllarında unix forumlarına düşümüş fakat 2005 yılında quake3 kodlarının paylaşılması ile patlama yapmış. Bu kadar bilgiden sonra önce kaynak sayfa :
http://en.wikipedia.org/wiki/Fast_inverse_square_root
Ve yöntem. En başlarda olay pek anlaşılamamış, zaten kod açıklamalarından göreceksiniz ;) Tüm olay şu sihirli sayıda : 0x5f3759df
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking [sic]
i = 0x5f3759df - ( i >> 1 ); // what the fuck? [sic]
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
32bit fp için 0x5f3759df
64bit fp için 0x5fe6eb50c7aa19f9.
y = * ( float * ) &i şurdaki yapılan işlem nedir? stm32 nin kütüphanelerindede *(x *) şeklinde çok tanımlama var burdada görünce dayanamadım sorim dedim.
trigonometrik işlemlerin çözümü için, seriye açılımları kullanılıyor, seriye açılımları birçoğunuz üniversitede mat2 dersinde görmüşsünüzdür, derleyiciler hangi seriye açılımı kullanıyor bilmiyorum ama arkadaşın da söylediği gibi taylor serisi kullanılan yöntemlerden biridir, bunun dışında mclourin serisi de vardır
Alıntı yapılan: NaMcHo - 11 Mayıs 2011, 10:39:39
y = * ( float * ) &i şurdaki yapılan işlem nedir? stm32 nin kütüphanelerindede *(x *) şeklinde çok tanımlama var burdada görünce dayanamadım sorim dedim.
O değişkenin adresinde tutulan değeri tam sayı olarak değil float olarak değerlendir demek istiyor.
Yanlış anlamadıysam, üzerinde bit işlemleri yapabilmek için öncelikle float y'nin long olduğunu varsayıp kaydırma yapmış. Sonra çıkan sonucu tekrar derleyiciye "bu aslında bir float'tır" diye göstermiş.
Alıntı yapılan: NaMcHo - 11 Mayıs 2011, 10:39:39
y = * ( float * ) &i şurdaki yapılan işlem nedir? stm32 nin kütüphanelerindede *(x *) şeklinde çok tanımlama var burdada görünce dayanamadım sorim dedim.
burada ilk yıldız , i nin gösterdiği adreste bulunan veriyi al ifadesidir. eğer * koymasaydı alacağımız değer i nin adresi olacaktı.
float ifadesi Tagli'nin dediği gibi tip dönüşüm ifadesidir. Sonucu float olaak değerlendir demek. float ifadesinin sağındaki * ise sonucun da bir pointer ile aktarılacağını ifade ediyor.
aşağıdaki kod parçacıkları eeproma float değer yazma ve okuma işi yaparlar. bu örnek belki durumu biraz daha iyi açıklar.
void ee_read_float(unsigned int addr , float *floatptr){
unsigned char edata;
unsigned int I;
for(I=0;I<4;I++){
edata=ee_read_byte(I+addr);
*((unsigned char *)floatptr+I) = edata;
}
}
void ee_write_long(unsigned int addr , long int *longptr){
unsigned char edata;
unsigned int I;
for(I=0;I<4;I++){
edata = *((unsigned char *)longptr+I);
ee_write_byte(addr+I,edata);
}
}
yukarıdakialgoritmada anlamadığım bir şey var.
const float threehalfs = 1.5F;
x2 = number * 0.5F;
buradaki 1.5F ve 0.5F daha önceden #define ile tanımlanmış sabitler mi? yoksa benim bilmediğim bir jargon mu?
Yani aslında yapılan iş bir çeşit derleyiciyi kandırmak. O alanlarda aslında float saklanıyor ama sen derleyiciye "Hayır, bu bir long. Sen işlemini ona göre yap." diyorsun. Elbette ilgili sayıyı long gibi alıp üzerinde işlem yaparsan, float değerine çok garip şeyler olabilir ;). Yukarıdaki kodu garip yapan şey de bu. Adam float sayıyı long gibi gösterip üzerinde garip işlemler yapmış...
Dikkat edilmesi gereken şey casting diye bilinen bu işlemin, değişken üzerinde değil, onun pointer'ı üzerinde yapılıyor olması. float değerini long'a cast etseydi, fazla kafa karıştırmazdı. Örneğin 5.256 ifadesi 5 olurdu. Ama pointer'ı cast etmesi bambaşka birşey.
Klein, bildiğim kadarıyla sonlarında bulunan F, o değerlerin float tipinde olduğunu gösteriyor derleyiciye. Varsayılan değer nedir bilmiyorum. Bilgisayarda genelde double oluyor, yani hiçbir şey eklemezsen sonuna. float olarak alınmasını istiyorsan özellikle F ekliyorsun sonuna. Bu tür şeyler genelde gerekmiyor ama bazen hiç ummadığın yerlerde kıllık çıkarabiliyor. Şu başlığı (https://www.picproje.org/index.php/topic,32584.0.html) bir incele.
int main(void)
{
long i=5;
float y;
y=*(float*)&i;
printf("%f",y);
getch();
arkadaşlar yukarıdaki kodu denemek için şöyle küçük bir program yazdım sonucunu 0.0fakat derleyici sonucu 0.000 olarak verdi nedeni nedir acaba?
Wikipedia'daki başlığı (http://en.wikipedia.org/wiki/Single_precision) bir incele (kodu bilgisayarda yazdığını varsayıyorum).
(http://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Float_example.svg/590px-Float_example.svg.png)
(http://upload.wikimedia.org/math/c/0/2/c02c37523ae1acfd150420c1e2355ba6.png)
Şimdi, kutuların içinde "00000000 00000000 00000000 00000101" olduğunu varsay, yani 5.
Olay kafamda yanlış canlanmadıysa, bunu float gibi düşündüğünde çok küçük bir sayı çıkacak. Bu yüzden ekrana 0 olarak basılıyor olabilir.
Alıntı yapılan: yamak - 11 Mayıs 2011, 19:56:22
int main(void)
{
long i=5;
float y;
y=*(float*)&i;
printf("%f",y);
getch();
arkadaşlar yukarıdaki kodu denemek için şöyle küçük bir program yazdım sonucunu 0.0fakat derleyici sonucu 0.000 olarak verdi nedeni nedir acaba?
Daha önceki mesajımda konuyu tam olarak açıklayamadım sanırım. Sebebi ise sonucu float olaak değerlendir demiş olmam. Aslında bu tanım tam olarak doğru değil. Eğer float ifadesinin sağında * olmasaydı , tanım doğru olacaktı. Doğru tanım float pointer olarak değerlendir olacak.
Şöyle:
eğer :
y= *(float) &i demiş olsaydık:
i şaretçisinin gösterdiği adresteki değeri , float olarak y değişkenine atacaktık. burada derleyici bir tip dönüşümü işlemi uygulayacaktı. ve sonucu doğru görecektin.
fakat biz dedik ki , sonucu float pointer ile gönder. Bu durumda sayıyı bir float dönüşüm işlemine tabi tutmadı. Sadece aldığu 4 baytlık değeri , aynen y değişkenine aktardı.
Float sayılar tamsayılar gibi değildir. tamsayılarda int ve char arasındaki fark sadece kapladıkları alan farkıdır.
Örneğin: int tipi bir değişkenin içeriği 135 ise , bu değişkenin ilk baytının içeriği 135 ikinci baytının içeriği ise 0 dır. bu durumda taşmamak kaydıyla int veya char tipi değişkenlerin içeriğini birbiri arasında takas edebiliriz.
Ama float sayılarda durum farklı. Float sayının ilk baytına 135 sayısını yazarsak , float olarak anlamsız bir sayı elde ederiz.
Bu sebeple , yukarıdaki kodda long sayının içeriğini4 bayt olarak doğrudan float sayının içine yazdığımız için sonuçta anlamsız bir sayı elde ettik. bazı printf kütüphaneleri anlamsız float sayıları 0 olarak gösterir. Bu sebeple 0 olarak görmüş olabilirsiniz.
Alıntı yapılan: Klein - 11 Mayıs 2011, 19:04:37
Alıntı yapılan: NaMcHo - 11 Mayıs 2011, 10:39:39
y = * ( float * ) &i şurdaki yapılan işlem nedir? stm32 nin kütüphanelerindede *(x *) şeklinde çok tanımlama var burdada görünce dayanamadım sorim dedim.
burada ilk yıldız , i nin gösterdiği adreste bulunan veriyi al ifadesidir. eğer * koymasaydı alacağımız değer i nin adresi olacaktı.
float ifadesi Tagli'nin dediği gibi tip dönüşüm ifadesidir. Sonucu float olaak değerlendir demek. float ifadesinin sağındaki * ise sonucun da bir pointer ile aktarılacağını ifade ediyor.
Şuda doğru olurmu
y pointer olarak tanımlanmış olsa
y=*(float)&i
ile
y = * ( float * ) &i
bunlar birbirine denkmidir?2. koddaki
y ise float
1.işlemde
float pointer olan
y ye i'nin adresindeki değeri float olarak gönderiyoruz
2.işlemde ise i'nin adresi içindeki değeri float pointer olarak float y değişkenine gönderiyoruz
2. yazdığımdan pek bişey anlayamadım şuanda :)
Yok ikisi aynı ifade değil. Bir önceki mesajda farkı açıkladım. Sorun ilk açıklamadaki eksik bir ifadeden kaynaklanıyor.
Alıntı YapDaha önceki mesajımda konuyu tam olarak açıklayamadım sanırım. Sebebi ise sonucu float olaak değerlendir demiş olmam. Aslında bu tanım tam olarak doğru değil. Eğer float ifadesinin sağında * olmasaydı , tanım doğru olacaktı. Doğru tanım float pointer olarak değerlendir olacak.
Şöyle:
eğer :
y= *(float) &i demiş olsaydık:
i şaretçisinin gösterdiği adresteki değeri , float olarak y değişkenine atacaktık. burada derleyici bir tip dönüşümü işlemi uygulayacaktı. ve sonucu doğru görecektin.
fakat biz dedik ki , sonucu float pointer ile gönder. Bu durumda sayıyı bir float dönüşüm işlemine tabi tutmadı. Sadece aldığu 4 baytlık değeri , aynen y değişkenine aktardı.
Float sayılar tamsayılar gibi değildir. tamsayılarda int ve char arasındaki fark sadece kapladıkları alan farkıdır.
Örneğin: int tipi bir değişkenin içeriği 135 ise , bu değişkenin ilk baytının içeriği 135 ikinci baytının içeriği ise 0 dır. bu durumda taşmamak kaydıyla int veya char tipi değişkenlerin içeriğini birbiri arasında takas edebiliriz.
Ama float sayılarda durum farklı. Float sayının ilk baytına 135 sayısını yazarsak , float olarak anlamsız bir sayı elde ederiz.
Bu sebeple , yukarıdaki kodda long sayının içeriğini4 bayt olarak doğrudan float sayının içine yazdığımız için sonuçta anlamsız bir sayı elde ettik. bazı printf kütüphaneleri anlamsız float sayıları 0 olarak gösterir. Bu sebeple 0 olarak görmüş olabilirsiniz.
Burada *(float)&i yapasaydık doğru olurdu demiştiniz.Fakat ifadenin şu şekilde olması gerekmez mi "(float)*&i"? Zaten dediğiniz gibi yaptığımda derleyici hata veriyo.
c esnek bir dil. çoğu zaman dizilim sonucun değişmesine neden olmuyorsa dizilime çok bakmaz.
örneğin const char x ile char const x arasında bir fark yok.
veya aşağıdaki 3 tanım da birbirinin aynı.
void abc(char unsigned *const _far c ){
}
void abc(char unsigned const *_far c ){
}
void abc(char unsigned const _far *c ){
}
çoğu derleyici de buna hata vermez.
bu yüzden bu dizilime hata verip vermeyeceğini kontrol etmedim. Hata vereceğini de düşünmemiştim açıkçası. Sonuçta orada önemi olan float pointere aktarma ile floata çevirip aktarma arasındaki farkı anlatmak olduğu için çok önesemedim yıldızın nerede olduğunu.
Sonuç olarak mesele şu:
Eğer *(tip*) &değişken şeklinde bir dizilim olursa , bu tip dönüşümü yapmadan verilen tipte bir pointer kullanarak içeriği verir.
eğer (tip) *&değişken şeklinde kullanılırsa tip dönüşümü yaparak verir.