- CORDIC adalah algoritme untuk menghitung fungsi trigonometri seperti
sin, cos, dan tan tanpa FPU atau tabel lookup besar, dengan mengubah operasi kompleks menjadi operasi yang berfokus pada penjumlahan dan bit shift
- Pendekatan ini lebih berguna di lingkungan embedded daripada sistem berperforma tinggi, terutama pada mikrokontroler berdaya rendah dan FPGA, sehingga nilainya sulit dinilai hanya dari sisi kecepatan
- Dengan memakai fixed-point alih-alih floating-point, kita bisa membagi 16 bit atas dari
int32_t sebagai bagian integer dan 16 bit bawah sebagai bagian pecahan untuk merepresentasikan kira-kira dari -32768.99997 sampai 32767.99997
- Dengan memutar vektor ke arah sudut target menggunakan sudut yang makin kecil, serta memakai 16 entri tabel
atan(2**-i) dan nilai awal x=39796, perkalian pada tiap iterasi bisa diganti dengan bit shift
- Jika sudut contoh
0.9152 diiterasikan 16 kali, galat absolut sin(0.9152) turun hingga 0.00000956, dan galat absolut cos(0.9152) hingga 0.0000434
Lingkungan komputasi yang cocok untuk CORDIC
- CORDIC adalah algoritme untuk menghitung fungsi trigonometri seperti
sin, cos, dan tan pada perangkat keras berdaya rendah
- Algoritme ini tetap dapat bekerja di lingkungan yang tidak memiliki FPU, yaitu unit operasi floating-point, atau yang sulit memakai tabel lookup besar
- Operasi nyatanya tersusun terutama dari penjumlahan dan bit shift sederhana
- Algoritme ini menggabungkan matematika vektor, trigonometri, konvergensi, dan gagasan ilmu komputer untuk mengaproksimasi fungsi kompleks dengan operasi sederhana
- Pada perangkat keras berperforma tinggi, teknik ini belum tentu benar-benar diperlukan
- Sasaran utamanya adalah lingkungan embedded
- Terutama cocok untuk mikrokontroler berperforma rendah dan FPGA
- Mungkin ada perangkat keras atau periferal yang lebih cepat, tetapi kecepatan bukan satu-satunya ukuran kegunaan
Representasi fixed-point untuk menghindari floating-point
- Fungsi seperti
sin(x) yang menghasilkan nilai antara -1.0 dan 1.0 tidak harus direpresentasikan dengan floating-point
- Fixed-point merepresentasikan bilangan rasional dengan menetapkan posisi titik desimal di dalam tipe integer
- Contoh ini membagi
int32_t menjadi 16 bit atas untuk bagian integer dan 16 bit bawah untuk bagian pecahan
- Dalam kasus ini, jangkauannya kira-kira dari
-32768.99997 sampai 32767.99997
- Bergantung pada di mana titik desimal diletakkan, kita bisa menukar jangkauan bagian integer dengan presisi bagian pecahan
- Nilai itu sendiri tetap berupa
int32_t, dan programmer memberi makna tambahan pada susunan bitnya
Konversi fixed-point dan operasi dasar
- Jika presisi pecahan adalah 16 bit, nilai float seperti
42.01 dapat diubah menjadi nilai fixed-point dengan mengalikannya dengan (1 << 16)
42.01 * (1 << 16) menjadi 2753167 jika di-cast ke int32_t
- Untuk mengubahnya kembali ke float, hitung
2753167 / (1 << 16) dan diperoleh sekitar 42.0099945
- Nilai seperti
1.5 juga bisa di-encode langsung tanpa sama sekali memakai floating-point
- Bagian integer
1 dinaikkan dengan (1 << 16)
- Separuh bagian pecahan bisa diletakkan pada
0x7fff, yaitu nilai tengah antara 0x0000 dan 0xffff
- Hasil pendekatan ini adalah
98303 dalam desimal
- Untuk nilai-nilai dengan faktor skala yang sama, penjumlahan dan pengurangan bekerja apa adanya
- Perkalian dilakukan dengan mengalikan dua nilai fixed-point lalu menggeser hasilnya ke kanan sebesar faktor skala
- Pembagian dapat memperoleh presisi tambahan dengan lebih dulu menggeser pembilang ke kiri sebesar faktor skala sebelum dibagi oleh penyebut
Mengaproksimasi fungsi trigonometri lewat rotasi vektor
- CORDIC adalah singkatan dari “co-ordinate rotation digital computer” dan dibuat pada pertengahan 1950-an
- Gagasan intinya adalah memutar vektor pada lingkaran satuan dengan sudut yang makin kecil, sehingga saat mencapai sudut target, komponen vektor menjadi nilai sinus dan cosinus
- Proses ini berjalan mirip pencarian biner
- Bergerak dengan sudut besar ke arah sudut target
- Memeriksa apakah target sudah terlewati
- Lalu mengulangi rotasi searah atau berlawanan arah jarum jam dengan sudut yang lebih kecil
- Sebagai contoh, saat mencari
sin(0.7), proses dimulai dari vektor awal (1, 0) dan target 0.7 radian
- Pertama, vektor diputar
0.7853 radian, yaitu 45˚, berlawanan arah jarum jam
- Sisa target menjadi
0.7 - 0.7853 = -0.0853
- Karena nilainya negatif, langkah berikutnya adalah memutar
0.3926 radian, yaitu 22.5˚, searah jarum jam
- Setelah itu, arah rotasi terus diubah mengikuti tanda sisa target dengan sudut yang makin kecil seperti
0.1963 radian
- Setelah 16 iterasi, vektor hampir tepat sejajar dengan sudut target semula, dan
y menjadi aproksimasi sin(a), sementara x menjadi aproksimasi cos(a)
Mengurangi operasi mahal dari matriks rotasi
- Rotasi vektor biasa menggunakan perkalian matriks yang melibatkan sinus dan cosinus
- CORDIC memakai identitas trigonometri untuk mengubah matriks rotasi agar berpusat pada
tan(a)
- Karena mula-mula digunakan sudut rotasi tetap seperti
45˚, 22.5˚, dan 11.25˚, nilai tan(a) dapat disiapkan lebih dulu dalam sebuah tabel
- Tabel ini hanya membutuhkan 16 buah
uint32_t, yaitu 64 byte
- Sebagai perbandingan, tabel
sin(x) yang tidak dioptimalkan dengan 4096 nilai dari -1 sampai 1 memerlukan 16KiB dan dianggap berpresisi lebih rendah
- Faktor
cos(a) yang muncul di depan pada setiap rotasi memang tetap ada, tetapi hasil perkalian semuanya konvergen ke sebuah konstanta
- Untuk sudut seperti
45˚, 22.5˚, dan 11.25˚, hasil perkalian ini sekitar 0.6366
- Konstanta ini cukup dikalikan sekali setelah seluruh iterasi selesai
Memilih sudut agar hanya tersisa shift dan penjumlahan
- Untuk menghilangkan perkalian, sudut dipilih agar hasil
tan(a) selalu berupa inverse power of two
- Untuk itu, dibuat tabel 16 entri yang berisi nilai
atan(2**-i) untuk tiap iterasi dari i=0 sampai 15
- Sudut rotasi yang benar-benar dipakai menjadi
45˚, 26.565˚, 14.036˚, 7.125˚, dan seterusnya
- Meskipun sudut-sudut ini tidak berkurang tepat setengah setiap kali, prosesnya tetap konvergen ke hasil yang benar
- Perkalian
tan(a) berubah menjadi bit shift sebesar nomor iterasi i
- Hasil perkalian faktor
cos(a) juga dihitung ulang sesuai pilihan sudut baru ini
- Nilainya sekitar
0.60725
- Dalam fixed-point 16 bit, nilainya menjadi
39796
- Jadi, alih-alih mengalikannya di akhir,
x pada vektor awal cukup diatur menjadi 39796 alih-alih 1
Prosedur algoritme
- Pada tahap prakomputasi, dibuat tabel yang setiap entrinya adalah
atan(2**-i), lalu masing-masing nilai dikonversi ke fixed-point
- Rumus konversinya adalah
atan(2**-i) * (1 << 16)
- Saat menghitung
sin atau cos, sudut input juga diubah ke fixed-point
- Contoh
0.9152 menjadi 0.9152 * (1 << 16) = 59978
- Keadaan awalnya adalah sebagai berikut
x = 39796
y = 0
z = 59978
z bukan bagian dari vektor, melainkan nilai untuk melacak sisa sudut target
- Tanda
z menentukan arah rotasi
- Jika
z >= 0, rotasi dilakukan berlawanan arah jarum jam dan z -= table[i] dijalankan
- Jika
z < 0, rotasi dilakukan searah jarum jam dan z += table[i] dijalankan
- Setiap iterasi hanya memakai penjumlahan, pengurangan, dan shift
>> i pada x dan y
if z >= 0:
x_next = x - (y >> i)
y_next = y + (x >> i)
z -= table[i]
else:
x_next = x + (y >> i)
y_next = y - (x >> i)
z += table[i]
x = x_next
y = y_next
Hasil konvergensi contoh dan topik yang tersisa
- Pada contoh
0.9152 radian, iterasi pertama memutar vektor sekitar 0.785 radian berlawanan arah jarum jam karena z bernilai positif
- Pada iterasi kedua,
z masih positif sehingga vektor kembali diputar sekitar 0.436 radian berlawanan arah jarum jam, tetapi kali ini melewati target
- Pada iterasi ketiga,
z menjadi negatif sehingga vektor diputar sekitar 0.244 radian searah jarum jam
- Pada iterasi keempat,
z tetap negatif sehingga vektor kembali diputar sekitar 0.124 radian searah jarum jam
- Saat perubahan sudut makin kecil, vektor bergerak bolak-balik di sekitar hasil sebenarnya sambil konvergen
- Setelah 16 iterasi,
y menjadi aproksimasi yang sangat dekat untuk sin(0.9152)
- Galat absolut sinus adalah
0.00000956
- Galat absolut cosinus pada
x adalah 0.0000434
- Masih ada topik yang belum dibahas
- Penanganan khusus yang diperlukan ketika sudut yang diminati berada di luar kuadran pertama atau keempat pada lingkaran satuan
- Variasi CORDIC yang dapat menghitung
tan, atan, asin, acos, sinh, cosh, tanh, sqrt, ln, e^x
- Algoritme terkait BKM yang dirancang untuk perhitungan logaritma dan eksponensial
- Rencananya, topik terkait akan dibahas lebih detail di Low Byte Productions YouTube channel
1 komentar
Komentar Hacker News
Penulis mengatakan ini terutama diterapkan pada hal seperti FPGA, tetapi juga bisa digunakan untuk pengembangan game atau simulasi fisika terdistribusi
Perhitungan floating-point sulit dibuat deterministik antarplatform, dan salah satu solusinya adalah menghindari floating-point sama sekali dengan mengimplementasikan engine fisika fixed-point
Untuk mengimplementasikan fungsi trigonometri, dibutuhkan sesuatu seperti CORDIC
Beberapa tahun lalu saya mulai membuat hal seperti ini untuk bersenang-senang, tetapi tidak pernah selesai, dan suatu hari saya ingin mencobanya lagi
https://randomascii.wordpress.com/2013/07/16/floating-point-...
Singkatnya, x87 punya beberapa keanehan, pengaturan seperti mode pembulatan dan flush-to-zero harus diseragamkan, prosesor lama tidak memiliki FMA, instruksi aproksimasi seperti
mmsqrtpstidak memiliki spesifikasi yang konsisten, dan compiler dapat menggabung ulang ekspresiUntuk rutin kecil atau library buatan sendiri, meski menyakitkan, masih mungkin menjamin agar hal-hal ini dihindari
IEEE-754 2008 memperjelas spesifikasi dan pada dasarnya mengandaikan kematian x87, dan pada 2024 kita benar-benar bisa menghindari x87
FMA juga merupakan bagian dari spesifikasi IEEE-754 2008, dan tersedia di prosesor modern termasuk Intel Haswell ke atas
Meski begitu, perbedaan arsitektur seperti AVX2 8-wide dan NEON 4-wide masih bisa jadi penghambat, tetapi jika memakai assembly atau intrinsic, atau C yang diverifikasi lewat Compiler Explorer maupun objdump, kita bisa melihat hasil keluarannya dan menilai, “ini akan konsisten”
“Sebenarnya, sebelum IEEE 754 menjadi standar populer seperti sekarang, fixed-point selalu digunakan. Tanyakan pada developer game yang bekerja kira-kira antara 1980 sampai 2000, mereka akan bisa menjelaskannya dengan rinci”
Library baru Rapier, yang menulis ulang nphysics, sebagai gantinya mengandalkan jaminan IEEE-754 2008 untuk menyediakan determinisme antarplatform
Karena itu ia tidak berjalan di platform lama, tetapi deterministik di platform modern termasuk wasm
Tentu saja tetap tidak bisa bergantung pada rutin fungsi transendental seperti sin dan cos yang disediakan masing-masing platform, dan harus mengimplementasikannya sendiri agar bekerja dengan cara yang sama di mana pun
Namun ini pendekatan yang memungkinkan selama tidak dijalankan pada platform yang tidak patuh standar
https://www.rustsim.org/blog/2020/06/01/this-month-in-rustsi...
https://rapier.rs/docs/user_guides/rust/determinism/
CORDIC dapat digunakan bukan hanya untuk menghitung atau menghasilkan sinus dan kosinus, tetapi juga untuk berbagai operasi seperti logaritma, eksponensial, akar kuadrat, besar vektor, konversi koordinat polar-kartesius, dan rotasi vektor
Penulis juga memberi isyarat tentang kemungkinan ini di bagian kesimpulan
Rasanya jika memakai kuaternion alih-alih matriks ortonormal yang ada, operasi berbasis CORDIC bisa dijalankan lebih efisien, yaitu dengan lebih sedikit siklus komputasi dan memori, sekaligus mengurangi galat
https://core.ac.uk/works/8439118
Di pelajaran prakalkulus SMA saya belajar deret Taylor, dan guru saya mengatakan fungsi trigonometri di kalkulator benar-benar diimplementasikan seperti itu
Setelah saya cari tahu, ternyata yang sebenarnya dipakai adalah CORDIC, dan saya bersenang-senang mengimplementasikannya dalam TI Basic
Itu bukan CORDIC, tetapi ada kemiripan dalam algoritmanya
http://files.righto.com/calculator/sinclair_scientific_simul...
Tulisan terkait implementasi hardware:
https://arxiv.org/pdf/2211.04053
https://hal.science/hal-01327460/document
https://archive.ll.mit.edu/HPEC/agendas/proc05/Day_1/Abstrac...
Ingin melihat bagaimana ini dibandingkan dengan implementasi fungsi trigonometri software·hardware umum pada beragam hardware dari tiap era
IoT dan komunikasi antar-mesin sedang tumbuh, dan jika melihat implementasi CORDIC serta efisiensi komputasinya, penggunaannya kemungkinan akan meningkat besar, jadi dibutuhkan referensi yang baik untuk implementasi yang benar dan optimal
Sebagai pengecualian, ada buku Prof. Omondi dan Prof. Deschamps
https://www.worldscientific.com/worldscibooks/10.1142/p1054
http://www.arithmetic-circuits.org/guide2fpga/vhdl_codes.htm
sin dan cos sering dipakai untuk rotasi vektor
Dalam kasus ini, trik CORDIC adalah menghindari perhitungan sin/cos/perkalian tradisional dan langsung memasukkan vektor yang akan diputar itu sendiri sebagai input CORDIC
Dengan begitu, CORDIC langsung menghasilkan vektor yang sudah diputar tanpa menghitung sin/cos atau melakukan perkalian bilangan kompleks
CORDIC sangat menonjol terutama saat latensi tidak terlalu penting
Jika tiap tahap komputasi dipipeline, throughput bisa sangat tinggi, sehingga cocok untuk digital mixing pada sistem nirkabel
Pada 2023, beberapa MCU modern punya FPU meski harganya murah
STM32G4 adalah contoh yang bagus, dan tidak seperti pada kasus MCU M0, kalau tidak mau memakai fixed-point maka bisa bebas memakai
f32Chip seperti ini bisa didapat di kisaran sekitar 1~2 dolar per MCU
Namun, G4 juga punya periferal hardware CORDIC yang mengimplementasikan algoritma ini untuk penggunaan fixed-point
Saya penasaran apakah ini terutama ditujukan untuk menghindari hilangnya presisi floating-point
Ia diprogram lewat register, tetapi bukan CPU yang mengimplementasikan CORDIC secara langsung; yang mengerjakannya adalah hardware khusus di dalam IC
STM32G4 termurah adalah STM32G441KBT6, dan jika dibulatkan harganya 4 dolar https://www.digikey.com/en/products/detail/microchip-technol...
Saya penasaran di mana bisa mendapatkannya di bawah 2 dolar
Di Digi-Key, chip Nuvoton nyaris turun di bawah 2 dolar jika beli 500 unit
Cepat, dan mampu menangani perkalian antara 64-bit sehingga presisi pembagian dan fungsi trigonometrinya cukup untuk sebagian besar penggunaan
Kalau perlu, presisinya juga bisa ditingkatkan lagi lewat software
Saya baru mengetahui CORDIC agak terlambat; sebelumnya saya banyak memakai fixed-point di dunia assembly 8-bit·16-bit demi performa dan determinisme
Setelah tahu, saya kaget
Cepat, dan kemampuan matematika yang dibutuhkan untuk memakainya secara berguna juga hanya yang dasar
Ini mengingatkan saya pada potongan kode yang cukup manis yang pernah saya ikuti dulu
Kami perlu mencari koordinat garis bagi dari sudut yang dibentuk oleh busur pada lingkaran satuan, dan koordinat
(x,y)untuk kedua lengannya sudah adaImplementasi yang lama adalah bongkahan trigonometrik: mengubah koordinat
(x,y)menjadi koordinat polar(r,θ), memastikanθyang dihitung berada di kuadran yang benar, lalu membagiθmenjadi dua dan mengubahnya kembali ke(x,y)Hasilnya, ada banyak pemanggilan fungsi trigonometri dan inversnya
Karena di Python bilangan kompleks bisa dipakai sebagai warga kelas satu, kami cukup mendefinisikan dua bilangan kompleks
z1dari(x1,y1)danz2dari(x2,y2), lalu mengambil rata-rata geometrik dari hasil kalinya,√(z1*z2), dan selesaiDi kode baru tidak ada fungsi trigonometri eksplisit, juga tidak ada konversi maupun invers konversi yang eksplisit
https://fgiesen.wordpress.com/2010/10/21/finish-your-derivat...
Tertulis, “memutar sebesar 22.75˚ sama dengan memutar 45˚ lalu memutar -22.5˚”, tapi kalau begitu bukankah hasilnya rotasi 22.5°?
Saya penasaran apakah itu kesalahan di tulisan, atau saya yang salah paham
Sistem octree karya Meagher terkenal karena hanya menggunakan aritmetika bilangan bulat tanpa perkalian atau pembagian bilangan bulat
“Operasi Boolean (gabungan, irisan, selisih), operasi geometri (pergeseran, penskalaan, rotasi), deteksi interferensi berdimensi-N, serta algoritme waktu linear yang efisien untuk penampilan termasuk penghilangan permukaan tersembunyi pada titik sembarang di ruang telah dikembangkan. Algoritme ini tidak memerlukan operasi floating-point, perkalian bilangan bulat, atau pembagian bilangan bulat”
https://doi.org/10.1016/0146-664X(82)90104-6
Berkat ini, lebih mudah membuat perangkat keras akselerasi grafis VLSI kustom yang cepat untuk representasi octree
Saya penasaran bagaimana performa CORDIC dibandingkan interpolasi kubik yang memakai tabel kecil atau interpolasi polinomial lainnya
Saya belajar bahwa synthesizer dengan sumber daya terbatas kadang memakai interpolasi kubik, mungkin pada masa ketika CORDIC masih relatif baru
Secara kasar, CORDIC memperoleh 1 bit presisi di setiap iterasi, jadi komputasinya tampak lebih mahal tetapi memakai ruang lebih sedikit daripada polinomial
Namun dari sisi ruang, perlu ditekankan bahwa biayanya bisa lebih murah daripada tabel lookup 4096 entri untuk
sin(x)yang ditunjukkan dalam tulisanBerkat simetri, yang diperlukan hanya 1/4 dari seluruh lingkaran
Jika memakai sudut berukuran byte, nilainya otomatis berputar sehingga praktis, dan
2^8sudah cukup memadai untuk rotasi di game 2DNamun untuk 3D itu tidak terlalu jauh berguna jika menginginkan gerakan yang halus