Karmaşık Sayı Türleri ve C++'nın std::complex'i
Bu sayfadaki bilgiler digitalmars.com sitesindeki eski bir yazıdan alınmıştır
Yazım güzelliği
C++'ta karmaşık sayı türleri şunlardır:
complex<float> complex<double> complex<long double>
C++'da sanal [imaginary] türler yoktur. D'de 3 karmaşık türe ek olarak 3 tane de sanal tür vardır:
cfloat cdouble creal ifloat idouble ireal
C++'nın karmaşık sayıları aritmetik sabitlerle etkileşebilirler, ama sanal değer kavramı olmadığı için sanal değerler eklemek için kurucu fonksiyon kullanmak gerekir:
complex<long double> a = 5; // a = 5 + 0i complex<long double> b(0,7); // b = 0 + 7i c = a + b + complex<long double>(0,7); // c = 5 + 14i
D'de sanal değer sabitlerinin sonuna i
karakteri eklenir. Yukarıdaki kodun eşdeğeri şudur:
creal a = 5; // a = 5 + 0i ireal b = 7i; // b = 7i c = a + b + 7i; // c = 5 + 14i
İçinde sabitlerin de olduğu daha uzun bir ifade:
c = (6 + 2i - 1 + 3i) / 3i;
C++'da şöyle yazılır:
c = (complex<double>(6,2) + complex<double>(-1,3)) / complex<double>(0,3);
veya C++'ya da bir sanal tür eklenmiş olsaydı şöyle:
c = (6 + imaginary<double>(2) - 1 + imaginary<double>(3)) / imaginary<double>(3);
Kısacası, D'de nn gibi bir sanal değer nni
olarak yazılabilir; complex<long double>(0,nn)
şeklinde nesne oluşturmaya gerek yoktur.
İşlemlerin etkinliği
C++'da sanal sayı türünün olmaması, 0 gerçel sayısının da işlemlerde gereksizce kullanılmasına neden olur. Örneğin D'de iki sanal değerin toplamı tek bir toplama işleminden oluşur:
ireal a, b, c;
c = a + b;
C++'da ise gerçel kısımlarının da toplandığı iki toplama işlemidir:
c.re = a.re + b.re; c.im = a.im + b.im;
Durum çarpma işleminde daha da kötüdür: tek bir çarpma işlemi yerine 4 çarpma ve 2 toplama işlemi:
c.re = a.re * b.re - a.im * b.im; c.im = a.im * b.re + a.re * b.im;
En kötüsü de bölme işlemidir: D'deki tek bir bölme işlemi yerine, C++'da normal bir gerçekleştirmede 1 karşılaştırma, 3 bölme, 3 çarpma, ve 3 toplama işlemi vardır:
if (fabs(b.re) < fabs(b.im)) { r = b.re / b.im; den = b.im + r * b.re; c.re = (a.re * r + a.im) / den; c.im = (a.im * r - a.re) / den; } else { r = b.im / b.re; den = b.re + r * b.im; c.re = (a.re + r * a.im) / den; c.im = (a.im - r * a.re) / den; }
Bu yavaşlıkları önlemek için sanal değer yerine C++'da double
kullanılabilir. Örneğin D'deki şu kod:
cdouble c; idouble im; c *= im;
sanal kısım için double
kullanılarak C++'da şöyle yazılabilir:
complex<double> c; double im; c = complex<double>(-c.imag() * im, c.real() * im);
Ama onun sonucunda da aritmetik işlemlerle kullanılabilen bir kütüphane türü kolaylığını artık geride bırakmış oluruz.
Anlam
Bütün bunların en kötüsü, sanal değer türünün eksikliği nedeniyle istemeden yanlış sonuçlar elde edilebilmesidir. Prof. Kahan'dan bir alıntı:
Fortran'da ve C/C++ derleyicileriyle gelen kütüphanelerde gereken karmaşık sayı fonksiyonları SQRT
ve LOG
, eğer IEEE 754 aritmetik işlemlerinde 0.0
'ın işaretini gözardı edecek şekilde gerçekleştirilirlerse, karmaşık sayı z
'nin negatif gerçel değer aldığı durumlarda artık
SQRT( CONJ( Z ) ) = CONJ( SQRT( Z ) )
ve
LOG( CONJ( Z ) ) = CONJ( LOG( Z ) )
eşitlikleri sağlanamaz. Karmaşık sayı aritmetiğinde işlemlerin gerçel ve sanal sayıların x + i*y
şeklindeki soyutlamaları yerine (x, y) gibi çiftler üzerinde yapıldıkları durumlarda bu tür bozuklukların önüne geçmek imkansızdır. Karmaşık sayı işlemlerinde çiftlerin kullanılması yanlıştır; bir sanal türe ihtiyaç vardır.
Buradaki anlamsal sorunlar şunlardır:
(sonsuz + i)
sonucunu veren(1 - sonsuz*i) * i
formülünü ele alalım. Burada ikinci çarpan tek başınai
olmak yerine(0 + i)
olursa, sonuç(sonsuz + NaN*i)
haline gelir; yani yanlış bir NaN oluşmuş olur. [Çevirenin notu: "not a number"'ın kısaltması olan NaN, geçersiz sayı değerlerini ifade eder.]- Ayrı bir sanal tür, 0'ın işaretini de taşıyabilir; bunlar branch cut'larla ilgili hesaplarda gerekirler.
C99 standardının Appendix G bölümü bu sorunla ilgili olarak bazı tavsiyelerde bulunur. Ancak o tavsiyeler C++98 standardına dahil olmadıklarından taşınabilirliklerine güvenilemez.
Kaynaklar
- How Java's Floating-Point Hurts Everyone Everywhere, Prof. W. Kahan and Joseph D. Darcy
- The Numerical Analyst as Computer Science Curmudgeon, Prof. W. Kahan
- "Branch Cuts for Complex Elementary Functions, or Much Ado About Nothing's Sign Bit" by W. Kahan, ch. 7 in The State of the Art in Numerical Analysis (1987) ed. by M. Powell and A. Iserles for Oxford U.P.