Wednesday, March 9, 2011

Membuat User Define Complex Datatype menggunakan Operator Overloading di C/C++

(Updated 2011/03/11 - menambahkan fungsi abs() dan conj())

Dalam pemodelan DSP (Digital Signal Processing), tak jarang kita membutuhkan operasi bilangan kompleks, misal (2 + 3i) * (0.2 + 3.5i). Operasi seperti ini sangat mudah dilakukan di Matlab, seperti berikut ini:

>> (2 + 3i) * (0.2 + 3.5i)


ans=


  -10.1000 + 7.6000i

Tapi berbeda halnya ketika menggunakan C/C++. Tapi jangan kuatir, sekompleks apapun tipe data dan operasinya, bisa kita buat di C/C++. Tipe data complex sebenarnya sudah ada di standard library , namun dengan alasan interoperability, kita bisa buat sendiri tipe datanya. Misal, jika ingin menggabungkan library fix-point nya SystemC dengan tipe data kompleks.

Operator overloading.

Operator overloading memungkinkan kita untuk menggunakan operasi yang sama pada jenis tipe data yang berbeda dan menghasilkan hasil yang berbeda. Contoh ekstrimnya begini:

int a = 1;
int b = 2;
int c = a + b; // c = 3

Kode diatas akan menghasilkan nilai c = 3. Akan tetapi kalau kita memiliki tipe data lain yang ada operator overloading untuk operasi penjumlahan (+) nya, maka hasilnya bisa beda sesuai keinginnan kita. Misal:

tipe_buatan_sendiri a = 1;
tipe_buatan_sendiri b = 2;
tipe_buatan_sendiri c = a + b; // c = -1 (haha)

Kalau saya overload operator penjumlahannya menjadi: + adalah a - b, maka kode diatas akan menghasilkan nilai c = -1, karena c = a + b = a - b (karena operasi + saya overload menjadi -) = 1- 2 = -1. Haha, seems mad =)).. OK, itu contoh ekstrimnya, kita masuk ke implementasi real-nya pada tipe data kompleks.

Tipe data kompleks.

Tipe data kompleks memiliki anggota real dan imaginer, kita buat dulu struct-nya.

struct mycomplex_t {
  int real, imag;


  //constructor
  mycomplex_t(int init_real, int init_imag)
  {
    real = init_real;
    imag = init_imag;
  }
};

Sekarang kita punya tipe data mycomplex_t, dan langsung bisa kita gunakan:

mycomplex_t a(1, 2); //real = 1, imag = 2
cout << a.real; //print nilai real dari a, yaitu = 1
cout << a.imag; //print nilai real dari a, yaitu = 2

Kode mycomplex_t a(1, 2) akan memanggil struct mycomplex_t, dang menginisiasi nilai real dan imag yang ada di struct tersebut menggunakan constructor. Lalu kita tambahkan operator penjumlahan menggunakan operator overloading dan copy contsructor:

struct mycomplex_t {
  int real, imag;

  //constructor
  mycomplex_t(int init_real, int init_imag)
  {
    real = init_real;
    imag = init_imag;
  }

  //operasi penjumlahan
  mycomplex_t operator + (const mycomplex_t& rhs) {
    mycomplex_t t(0, 0);
    t.real = this->real + rhs.real;
    t.imag = this->imag + rhs.imag;
    return t;
  }

};

Dengan begini kita bisa lakukan operasi sebagai berikut:

mycomplex_t a(1, 2); //real = 1, imag = 2
mycomplex_t b(3, 4); //real = 3, imag = 4
mycomplex_t c(0, 0); //real = 0, imag = 0

c = a + b;

cout << c.real; //print nilai real dari c, yaitu = 4
cout << c.imag; //print nlai real dari c, yaitu = 6

OK, sampai disini kita sudah mempunyai tipe data kompleks buatan sendiri. Agar tipe data ini bisa dipakai sepenuhnya, kta lengkapi operasi-operasi yang mungkin dalam bilangan komplek:

1. + (penjumlahan)
2. - (pengurangan)
3. * (perkalian)
4. / (pembagian)
5. abs (absolut)
6. conj (konjugasi)
7. = (sama dengan)

Berikut operator overloadingnya:

  //operasi penjumlahan
  mycomplex_t operator + (const mycomplex_t& rhs) {
    mycomplex_t t;
    t.real = this->real + rhs.real;
    t.imag = this->imag + rhs.imag;
    return t;
  }

  //operasi pengurangan
  mycomplex_t operator + (const mycomplex_t& rhs) {
    mycomplex_t t;
    t.real = this->real - rhs.real;
    t.imag = this->imag - rhs.imag;
    return t;
  }

  //operasi perkalian
  mycomplex_t operator * (const mycomplex_t& rhs) {
    mycomplex_t t;
    t.real = (this->real * rhs.real) - (this->imag * rhs.imag);
    t.imag = (this->imag * rhs.real) + (this->real * rhs.imag);
    return t;
  }

  //operasi pembagian
  mycomplex_t operator / (const mycomplex_t& rhs) {
    mycomplex_t t;
    int div = ((rhs.real * rhs.real) + (rhs.imag * rhs.imag));
    t.real = ((this->real * rhs.real) + (this->imag * rhs.imag)) / div;
    t.imag = (this->imag * rhs.real) - (this->real * rhs.imag) / div;
    return t;
  }


  //operasi absolut
  mycomplex_t& abs()
  {
    int t;
    t = (this->real * this->real) + (this->imag * this->imag);
    float x1, x2;
    int i;
    while( (i*i) <= t )
          i+=0.1;
    x1=i;
    for(int j=0;j<10;j++)
    {
      x2=m;
      x2/=x1;
      x2+=x1;
      x2/=2;
      x1=x2;
    }
    return x2;    
  }

  //operasi konjugasi
  mycomplex_t& conj() {
    this->real = rhs.real;
    this->imag = - (rhs.imag);
    return *this;
  }



  //operasi sama dengan
  mycomplex_t& operator = (const mycomplex_t& rhs) {
    this->real = rhs.real;
    this->imag = rhs.imag;
    return *this;
  }

Sekarang, mari kita coba operasi-operasi tersebut:

mycomplex_t a(1, 2);
mycomplex_t b(3, 4);
mycomplex_t c(0, 0);

c = a + b;
c = a - b;
c = a * b;
c = a / b;
c = a.abs();
c = a.conj();

Bagaimana hasilnya? OK, bukan? Tapi tunggu dulu, sampai disini masih ada keanehan, kita harus inisiasi nilai tiap kali mau membuat variabel baru, padahal sebenarnya kita belum mengisi nilai variabel tersebut. Seperti dalam contoh: mycomplex_t c(0, 0). Hal ini karena constructor dalam struct-nya memakai parameter:

  //constructor
  mycomplex_t(int init_real, int init_imag)
  {
    real = init_real;
    imag = init_imag;
  }

Agar kita bisa membuat variabel dengan mycomplex_t c; saja, solusinya buat satu constructor lagi yang tanpa parameter tapi initial value-nya 0.

  //constructor
  mycomplex_t()
    : real(0), imag(0)
  {  }

Enjoy! (Ya2n)

3 comments:

  1. wow..mantab yan...
    operasi kompleks seperti inilah yang sangat dibutuhkan dalam dunia simulasi DSP.

    thanks infonya mantab!!

    ReplyDelete
  2. yayan,, saya sudah mencoba code-codemu di atas.
    cuma masih ada yang terasa aneh. Untuk kasus pembagian, hasil yang diperoleh dari code C dengan matlab cukup berbeda. Ndak beda jauh sih, cuma jika hasil tersebut kemudian menjadi inputan proses operasi aritmatik lainnya, kemungkinan hasil akhirnya akan menjadi semakin jauh berbeda.

    contoh :
    (2 + 3i) / (0.2 + 3.5i)

    untuk matlab menghasilkan : 0.8869 - 0.5207i
    sedangkan C menghasilkan : 0.908333 - 0.533333i

    ReplyDelete
  3. wah ternyata memang ada bug di bagian ini proses pembagian:
    int div = ((rhs.real * rhs.real) + (rhs.imag * rhs.imag));

    harusnya tipe datanya bukan 'int' tetapi 'double' menjadi :
    double div = ((rhs.real * rhs.real) + (rhs.imag * rhs.imag));

    oke dah solved...^_^

    ReplyDelete